И снова сказ о том, как я парсер свой оптимизировал.
Решил я из интереса написать http-forwarder, для этого мне нужно из запроса достать заголовки Host, Content-Length и Transfer-Encoding (Host чтобы знать, кому перенаправлять; Content-Length и Transfer-Encoding - чтобы знать, что делать с телом запроса). Решил я для этого использовать
Неплохо. Посмотрел на свой парсер в индиге - 1.7гб/с. Уныленько. Переломал все к чертям, позаменял на
Так а в чем же прикол, собственно? Да суть вся в том, что
Решил я из интереса написать http-forwarder, для этого мне нужно из запроса достать заголовки Host, Content-Length и Transfer-Encoding (Host чтобы знать, кому перенаправлять; Content-Length и Transfer-Encoding - чтобы знать, что делать с телом запроса). Решил я для этого использовать
bytes.IndexByte(). Результат (после пары часов пост-оптимизирования, конечно же) - 11.7gb/s throughput. Неплохо. Посмотрел на свой парсер в индиге - 1.7гб/с. Уныленько. Переломал все к чертям, позаменял на
bytes.IndexByte(). Стало 4.5гб/с. Уже лучше, хоть и все еще уныленько - но эту проблему я пока решаю.Так а в чем же прикол, собственно? Да суть вся в том, что
bytes.IndexByte() для х86 архитектур использует SIMD, позволяя, тем самым, обходить строки гооораздо быстрее. Собственно, весь выигрыш и сводится к тому, насколько грамотно получится вкрутить эту штуку себе в код. В парсере для http-forwarder'а, например, большую часть данных просто пропускают мимо ушей, потому что основных (и самых важных заголовков) - очень маленькое подмножество, благодаря чему и получается достичь относительно высокой производительности. В индиге отставание в практически 3 раза, но уже потому, что приходится гораздо больше данных копировать - все же тут парсеру приходится быть немного более general-purpose, и извлекать больше деталей из запроса. А, ну и путь запроса много сжирает, да.Интерпретатор, совместимый с go1.19 и go1.20. Интересный проект, чтобы быстро проверить идею - отнюдь никаких оптимизаций не предусмотрено, и даже никакой виртуальной машины для исполнения нет
GitHub
GitHub - traefik/yaegi: Yaegi is Another Elegant Go Interpreter
Yaegi is Another Elegant Go Interpreter. Contribute to traefik/yaegi development by creating an account on GitHub.
👍1
https://github.com/golang/go/issues/61897
Говорят, стоит ожидать. Штош, было бы замечательно увидеть итераторы, наконец, и в стандартной библиотеке
Говорят, стоит ожидать. Штош, было бы замечательно увидеть итераторы, наконец, и в стандартной библиотеке
GitHub
iter: new package for iterators · Issue #61897 · golang/go
We propose to add a new package iter that defines helpful types for iterating over sequences. We expect these types will be used by other APIs to signal that they return iterable functions. This is...
Начал замечать, что с тех пор, как добавили женерики - начали добавлять и интересные пакеты. Вроде все того же slice, арены (отнюдь не уверен во взаимосвязи), итераторы планируют. Может, пустые интерфейсы - и правда не настолько хорошее решение by design?
👏1
Что делать
А в чем прикол бустить каналы премиум-подписками?
Теперь и с каналов ливать. Ну что ж ты будешь делать
😁1🐳1
Переписываю gzip-декомпрессор из стд либы (пришлось, потому что иначе очень неудобно использовать в моем случае). Тут есть такая штука: если стоит флаг extra - следующие два байта после заголовков - длина данных, и непосредственно сами данные. Так вот: возможно, это не такой уж и hot-path, но на это все равно аллоцируют каждый раз новый слайс.
Собственно, а в чем вопрос? Так можно ведь буфер потом переиспользовать (в худшем случае - реаллоцировать под нужный размер, но это никак не будет отличаться от текущего решения). Просто сохранить его в структуре Reader. Ридер ведь переиспользуют! Я и уверен, что много кто и переиспользует, и хуже это явно не сделает. Так почему? Почему так?
Собственно, а в чем вопрос? Так можно ведь буфер потом переиспользовать (в худшем случае - реаллоцировать под нужный размер, но это никак не будет отличаться от текущего решения). Просто сохранить его в структуре Reader. Ридер ведь переиспользуют! Я и уверен, что много кто и переиспользует, и хуже это явно не сделает. Так почему? Почему так?
Forwarded from Ivan Sokolov
в C++20 можно легально заставить работать такой синтаксис:
5_m + 2 // вызывает ваш произвольный operator+. суффикс может быть любым начинающимся с _
Ivan Sokolov
в C++20 можно легально заставить работать такой синтаксис: 5_m + 2 // вызывает ваш произвольный operator+. суффикс может быть любым начинающимся с _
Ну это пиздец. Но круто. Но пиздец. Но...
Как я оптимизировал flate на 5-10мб/с
Пришлось копаться с внутренностями стандартного gzip компрессора, и он под капотом flate использует (что логично, ведь gzip - это просто надстройка в виде пары заголовков для flate-потока). Там, в декомпрессоре, было одно интересное поле -
Собственно, почему это странно? Потому что вызов функции по указателю (а не по идентификатору) - это indirect call, и тут уже дело конкретно в железе. Потому что для совершения такого вызова, процессору сначала придется обратиться по адресу в памяти, на который указывает наш func ptr. В С, например, компилятор может соптимизировать это так, чтобы разницы в производительности не было (предварительно положив адрес в регистр).
Да вот только мы не в С :). Я даже попробовать сделать бенчмарк - и правда, обычный вызов справляется за 0.2нс. В то время, как indirect-вариант - за 1.1нс. Стоит сделать помарку: 0.2нс - это всегда подозрительно, и, скорее всего, так и есть, потому что в измеряемой мною функции сразу производился возврат, и результат никуда не присваивался - компилятор мог просто-напросто вырезать такой вызов. Поэтому я повторил с флагом -N (дабы отключить оптимизации), и результат стал 1.1нс и 1.9нс соответственно. То есть, разница все еще присутствует.
Так какой из этого можно сделать вывод? Вызов по func ptr дороже. В пределах пикосекунд, но есть. И именно это и сыграло роль (хоть и минимальную, особенно смотря на коэффициент).
Вот ссылка на мой PR, если интересно. Можете посмотреть там сравнения до и после патча. А еще, там есть интересный момент, связанный с занулением статического массива:)
Пришлось копаться с внутренностями стандартного gzip компрессора, и он под капотом flate использует (что логично, ведь gzip - это просто надстройка в виде пары заголовков для flate-потока). Там, в декомпрессоре, было одно интересное поле -
step func(*decompressor) . Читай - указатель на функцию. Туда подставлялся метод декомпрессора, который должен быть использован для следующего шага (и делал он это довольно странным образом). Собственно, почему это странно? Потому что вызов функции по указателю (а не по идентификатору) - это indirect call, и тут уже дело конкретно в железе. Потому что для совершения такого вызова, процессору сначала придется обратиться по адресу в памяти, на который указывает наш func ptr. В С, например, компилятор может соптимизировать это так, чтобы разницы в производительности не было (предварительно положив адрес в регистр).
Да вот только мы не в С :). Я даже попробовать сделать бенчмарк - и правда, обычный вызов справляется за 0.2нс. В то время, как indirect-вариант - за 1.1нс. Стоит сделать помарку: 0.2нс - это всегда подозрительно, и, скорее всего, так и есть, потому что в измеряемой мною функции сразу производился возврат, и результат никуда не присваивался - компилятор мог просто-напросто вырезать такой вызов. Поэтому я повторил с флагом -N (дабы отключить оптимизации), и результат стал 1.1нс и 1.9нс соответственно. То есть, разница все еще присутствует.
Так какой из этого можно сделать вывод? Вызов по func ptr дороже. В пределах пикосекунд, но есть. И именно это и сыграло роль (хоть и минимальную, особенно смотря на коэффициент).
Вот ссылка на мой PR, если интересно. Можете посмотреть там сравнения до и после патча. А еще, там есть интересный момент, связанный с занулением статического массива:)
GitHub
Reduce allocations in flate decompressor and minor code improvements by fakefloordiv · Pull Request #869 · klauspost/compress
decompressor.step is now enumeric instead of function pointer. This reduces ALL allocations in decompressor benchmarks to constant 1 (previously this number differed for each case). However, it doe...
🔥1
func sum(a, b int) (c int) {
с = a + b
defer func() {
с++
}()
return
}
Что вернет эта функция в случае
sum(5, 5)? Для многих будет очевидно, что 11 - записали в c сумму, и в дефере инкрементировали. Окей, а если так?
func sum(a, b int) (c int) {
result := a + b
defer func() {
result++
}()
return result
}
казалось бы, ситуация практически 1 в 1. А нет: здесь дефер уже никак не влияет на возвращенный результат сложения. Почему так?
Давайте рассмотрим еще один, показательный случай:
func sum(a, b int) (c int) {
result := a + b
defer func() {
с++
}()
return result
}
Что мы имеем? Складываем а и б в переменную result, после чего возвращаем ее. А в дефере мы инкрементируем, казалось бы, ничего общего не имеющую с результатом возврата переменную с. Но что мы видим?
Ba-dum-tsss. Имеем снова
sum(5, 5) == 11! Вот это уже интересно. А теперь давайте разберемся, что же все-таки это такое, именованный возврат?Начать стоит с того, что у нас есть стэк. При вызове функции, мы аллоцируем новый стэкфрейм функции. При возврате, мы пишем значение в стэкфрейм caller'a и делаем jmp к инструкции, на которой тот остановился. Соответственно, когда мы возвращаем во втором примере переменную result, она копируется туда, где потом будет использоваться на месте вызова. И как раз туда, куда она копируется, и указывает с!
Поэтому, в последнем примере, когда мы инкрементируем с - он уже равен result, хоть мы явно этого и не делали. Получается, мы инкрементируем значение прямо на стэкфрейме caller'a. Это, к слову, также дает нам ответ на вопрос, почему при именованном возврате из функции можно сделать просто пустой return.
А теперь самое (не)интересное: это как-то влияет на перфоманс?
Я не знаю.
Я наконец дополировал индигу до состояния, когда готов показать.
Фич на данный момент маловато, равно как и миддлварей. Покрытие тестами не полное, но большая часть протестирована. Однако мне нужен фидбек, поэтому если имеете предложения по поводу функционала - буду рад. Если найдете багу - буду вдвойне рад. Если поможете с написанием функционала - буду рад в квадрате.
Линк - github.com/indigo-web/indigo
Внизу ридми лежит актуальная дока. Постарался в ней описать все основные моменты. Исправления и предложения по ней также принимаю
Фич на данный момент маловато, равно как и миддлварей. Покрытие тестами не полное, но большая часть протестирована. Однако мне нужен фидбек, поэтому если имеете предложения по поводу функционала - буду рад. Если найдете багу - буду вдвойне рад. Если поможете с написанием функционала - буду рад в квадрате.
Линк - github.com/indigo-web/indigo
Внизу ридми лежит актуальная дока. Постарался в ней описать все основные моменты. Исправления и предложения по ней также принимаю
GitHub
GitHub - indigo-web/indigo: Indigo is a blazingly fast web-framework
Indigo is a blazingly fast web-framework. Contribute to indigo-web/indigo development by creating an account on GitHub.
🔥1
Хотел написать пост, как оптимизировал индигу, и почему она вообще такая быстрая (но бенчмарков не будет). Но это скучно - все сводится к "думай, goto, simd".
Зато, как всем известно, что популярнее всего в интернетах? Бугурты!
Я хочу накинуть говна на го. А мне есть что сказать за те два года, что я пишу на этом языке. Каждому из пунктов я уделю отдельный пост, и я надеюсь, что вы будете не согласны (в противном случае, будет грустно)
Зато, как всем известно, что популярнее всего в интернетах? Бугурты!
Я хочу накинуть говна на го. А мне есть что сказать за те два года, что я пишу на этом языке. Каждому из пунктов я уделю отдельный пост, и я надеюсь, что вы будете не согласны (в противном случае, будет грустно)
0. Женерики
А нулевой потому, что это извечная проблема. Ну, была, их все-таки добавили. Спустя 11 лет после релиза, если что.
Ладно, временили 11 лет (можно сказать, я с 4 лет ждал их появления). Имеем по итогу ПОЛОВИНУ стд либы в пустых интерфейсах. Почему это хуево? Да потому, что мы таким образом избегаем от типосистемы. Она настолько примитивная, что даже стд либу с ней нормально сделать не могли, вы только вдумайтесь - а вам на этом еще круды пилить. Хорошо, ладно с той стд либой, которую мы все так любим (обмазанная any синк мапа, контексты, сортировки). На это посмотрели крудошлепы, и подумали: заебца! И знаете что? Мы теперь имеем тонну легаси с активным эксплуатированием any.
Ладно, я немного лукавлю. В стд либе одни кор мейнтейнеры берут any, потому что другим фИлОсОфИя Не ПоЗвОлЯеТ, видите ли, кривая вхождения станет выше (настолько низкого мнения они о круд-макаках). Но нахуя они крудошлепам? Я, конечно, занимаюсь другим профилем, дроча на перфоманс в ядре вебфреймворка, но практически всегда выбор обусловлен "быстрее нашлепать". Практически всегда есть способ реализовать задачу, не прибегая к стиранию типов. Но пРиДеТсЯ дУмАтЬ, бОлЬшЕ вРеМеНи ПоТрАтИтЬ. Извечная болезнь гофера. В итоге имеем сужение типа, посредством явного его приведения либо к более узкому интерфейсу, либо - что еще хуже - к конкретному не-интерфейсному типу. Что мы получаем? Чуть проебались - паника. Заслужено? Полностью. Можно было избежать? Абсолютно.
Скажите мне, пожалуйста, зачем? Зачем было столько ждать? Каким хуем команда инженеров из гугла, при том с одним из отцов С во главе, пришла к выводу, что полиморфизм нам нахуй не нужен? Да, есть видео, на котором Роб Пайк прямо заявляет, что язык был создан для гугломакак. Но неужто расчет был на настолько макак, что они даже программировать не умеют?
А нулевой потому, что это извечная проблема. Ну, была, их все-таки добавили. Спустя 11 лет после релиза, если что.
Ладно, временили 11 лет (можно сказать, я с 4 лет ждал их появления). Имеем по итогу ПОЛОВИНУ стд либы в пустых интерфейсах. Почему это хуево? Да потому, что мы таким образом избегаем от типосистемы. Она настолько примитивная, что даже стд либу с ней нормально сделать не могли, вы только вдумайтесь - а вам на этом еще круды пилить. Хорошо, ладно с той стд либой, которую мы все так любим (обмазанная any синк мапа, контексты, сортировки). На это посмотрели крудошлепы, и подумали: заебца! И знаете что? Мы теперь имеем тонну легаси с активным эксплуатированием any.
Ладно, я немного лукавлю. В стд либе одни кор мейнтейнеры берут any, потому что другим фИлОсОфИя Не ПоЗвОлЯеТ, видите ли, кривая вхождения станет выше (настолько низкого мнения они о круд-макаках). Но нахуя они крудошлепам? Я, конечно, занимаюсь другим профилем, дроча на перфоманс в ядре вебфреймворка, но практически всегда выбор обусловлен "быстрее нашлепать". Практически всегда есть способ реализовать задачу, не прибегая к стиранию типов. Но пРиДеТсЯ дУмАтЬ, бОлЬшЕ вРеМеНи ПоТрАтИтЬ. Извечная болезнь гофера. В итоге имеем сужение типа, посредством явного его приведения либо к более узкому интерфейсу, либо - что еще хуже - к конкретному не-интерфейсному типу. Что мы получаем? Чуть проебались - паника. Заслужено? Полностью. Можно было избежать? Абсолютно.
Скажите мне, пожалуйста, зачем? Зачем было столько ждать? Каким хуем команда инженеров из гугла, при том с одним из отцов С во главе, пришла к выводу, что полиморфизм нам нахуй не нужен? Да, есть видео, на котором Роб Пайк прямо заявляет, что язык был создан для гугломакак. Но неужто расчет был на настолько макак, что они даже программировать не умеют?
👎3