Вчера взахлёб прочитал про Regex в Rust
https://blog.burntsushi.net/regex-internals/
Очень подробно про разбор выражений, проблем с юникодом, поиски подстрок, автоматы, simd ускорения и тд.
В моей практике Rust Regex до версии 1.6-1.7 был достаточно слабым решением в мире регулярок, но со временем стало получше.
Один из способов сделать хорошо описан в статье -- попробуйте какие-то большие куски имплементации вытащить как API. Можно как debug API как минимум. Имплементации и интерфейсы становятся намного чище после таких упражнений.
BurntSushi крутой чувак, очень упертый и кажется на дистанции это хорошо окупается. Всем советую прочитать статью от начала и до конца вдумчиво
https://blog.burntsushi.net/regex-internals/
Очень подробно про разбор выражений, проблем с юникодом, поиски подстрок, автоматы, simd ускорения и тд.
В моей практике Rust Regex до версии 1.6-1.7 был достаточно слабым решением в мире регулярок, но со временем стало получше.
Один из способов сделать хорошо описан в статье -- попробуйте какие-то большие куски имплементации вытащить как API. Можно как debug API как минимум. Имплементации и интерфейсы становятся намного чище после таких упражнений.
BurntSushi крутой чувак, очень упертый и кажется на дистанции это хорошо окупается. Всем советую прочитать статью от начала и до конца вдумчиво
burntsushi.net
Regex engine internals as a library - Andrew Gallant's Blog
I blog mostly about my own programming projects.
1. Сначала ты пишешь о BurntSushi, а потом BurntSushi пишет тебе https://github.com/BurntSushi/memchr/pull/114. Статье указанной в PR почти год, а всё ещё раз в несколько месяцев меня кто-нибудь тегает.
2. Может, кто-то знает литературу по кешам, где eviction policies как-нибудь учатся по тому насколько тяжело будет бекенду отвечать на запрос промаха? Ситуация такая: иметь 90% попаданий лучше, чем 85% попаданий, но если 10% промахов нагибают бекенд в два раза больше, то кажется можно выбрать второй. Хотелось бы узнать, умеет ли что-то наука тут или есть ли какие-нибудь датасеты по тому как ведут себя кеши и их бекенды.
3. Поизучал я тут Iguana compression. Заявленные цифры действительно хорошо выглядят на реальных тестах. Сок Iguana заключается в том, что они очень много используют предпосчитанных масок вместе с инструкцией VPTERNLOGD. Эта инструкция расшифровывается как vector packed ternary logic. Фактически умеет делать тернарные операторы по битам 512 битных регистров -- по маске брать биты из одного или другого регистра, чего не было так быстро до AVX-2. Также ещё используются VPCOMPRESSD и тем самым хорошо умеет упаковывать биты как PEXT для скаляров. По формату им надо переводить base254 в base256 и обратно, что тоже делается табличками. В целом ощущение, что AVX-512 сейчас очень сильно растёт. Скажем, avx-512-sort и avx-512-partial-sort достаточно хорошо обгоняют мою библиотеку https://github.com/danlark1/miniselect -- хотя моя дала много буста тому же кликхаусу. Но пока всё для чисел, для более сложных компараторов не так всё просто.
Скептически долгое время относился к AVX-512, но кажется начинаем понимать как использовать для реальных задач. Интересные прорывы есть.
2. Может, кто-то знает литературу по кешам, где eviction policies как-нибудь учатся по тому насколько тяжело будет бекенду отвечать на запрос промаха? Ситуация такая: иметь 90% попаданий лучше, чем 85% попаданий, но если 10% промахов нагибают бекенд в два раза больше, то кажется можно выбрать второй. Хотелось бы узнать, умеет ли что-то наука тут или есть ли какие-нибудь датасеты по тому как ведут себя кеши и их бекенды.
3. Поизучал я тут Iguana compression. Заявленные цифры действительно хорошо выглядят на реальных тестах. Сок Iguana заключается в том, что они очень много используют предпосчитанных масок вместе с инструкцией VPTERNLOGD. Эта инструкция расшифровывается как vector packed ternary logic. Фактически умеет делать тернарные операторы по битам 512 битных регистров -- по маске брать биты из одного или другого регистра, чего не было так быстро до AVX-2. Также ещё используются VPCOMPRESSD и тем самым хорошо умеет упаковывать биты как PEXT для скаляров. По формату им надо переводить base254 в base256 и обратно, что тоже делается табличками. В целом ощущение, что AVX-512 сейчас очень сильно растёт. Скажем, avx-512-sort и avx-512-partial-sort достаточно хорошо обгоняют мою библиотеку https://github.com/danlark1/miniselect -- хотя моя дала много буста тому же кликхаусу. Но пока всё для чисел, для более сложных компараторов не так всё просто.
Скептически долгое время относился к AVX-512, но кажется начинаем понимать как использовать для реальных задач. Интересные прорывы есть.
GitHub
Add initial aarch64 neon support by redzic · Pull Request #114 · BurntSushi/memchr
This PR adds a NEON implementation of all mem{r}chr functions. This PR does not add NEON support for memmem, as deeper changes in the code are needed for the Vector trait for it to work efficiently...
1. https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-performance-extensions-apx.html
Тут Intel выложили статью о том как они будут менять архитектуру ассемблера. Мне честно очень понравилось, будет выглядеть более похоже на Arm:
* Теперь будет 32 логических регистра. Тут уменьшатся в разы инструкции перекладываний со стека и на стек. Тем не менее, результаты могут быть не такими воодушевляюшими, так как современные процессоры имеют по 200-350 физических регистров (без учёта SIMD регистров) и алгоритмы переименования давным давно хорошо справляются со сложными задачами. Тем не менее, можно будет не использовать такие сложные алгоритмы
* push2, pop2: быстрее будем класть и брать со стека. Тут можно посмотреть на LDP/STP инструкции из Arm о которых я писал.
* 3 argument instructions. Инструкции будут иметь 3 аргумента, теперь на надо будет складывать память с регистром и мувать в другой регистр, можно сразу класть в другой. Тоже Arm так делает давным давно. Тут скорее всего лучше будет компиляторам такие инструкции производить.
* Conditional stores/loads -- это интересно, на Arm такого нет, я думаю хорошо взлетит во всяких compression/sorting
* Унификация SIMD. Intel тоже хочет быть похожим на SVE в Армах с непостоянной максимальной шириной максимального регистра. Поэтому вы можете спросить какая версия SIMD доступна и кодировать инструкции с помощью EVEX префикса. И даже не придётся компилировать 2 копии.
2. Тут нашли удивительную уязвимость с помощью фаззинга AMD процессоров https://lock.cmpxchg8b.com/zenbleed.html: более менее мои Ctrl+C Ctrl+V оно нашло за несколько секунд эксплуатации. Идея: давайте на разных процессорах запустим случайно сгенерированные программы, а также программы, где между инструкциями стоят lfence/mfence инструкции. Последние убивают instruction level parallelism, а значит и много оптимизаций вроде переименования регистров, speculative execution. После этого можно сверить все результаты. Если убрать все инструкции результат которых не определён, скажем, bsf (Bit Scan Forward при нуле не определён), то можно честно сравнивать выходы. Так и нашли этот баг. Удивительно крутая история.
3. Переписал Iguana Compression c Go на C++ (выложим код скорее всего). Получилось попробовать в более боевых условиях. Результаты хорошие, но не 2-3x заявленных в бенчмарках. По уровню сжатия версия без энтропийного кодирования чуть получше стандартного LZ4, а энтропийное где-то между ZSTD:2-3. В целом ничего не мешает увеличивать сжатие, так как алгоритм из LZ семейства и все алгоритмы сжатия из ZSTD можно перенести в Iguana. На Icelake писали, что где-то прибавка в 20-30%, а от себя скажу, что на Zen4 примерно всё ровно с ZSTD. AVX вещи у AMD всегда отстают на где-то 1 поколение от Intel. Догонят :)
Тут Intel выложили статью о том как они будут менять архитектуру ассемблера. Мне честно очень понравилось, будет выглядеть более похоже на Arm:
* Теперь будет 32 логических регистра. Тут уменьшатся в разы инструкции перекладываний со стека и на стек. Тем не менее, результаты могут быть не такими воодушевляюшими, так как современные процессоры имеют по 200-350 физических регистров (без учёта SIMD регистров) и алгоритмы переименования давным давно хорошо справляются со сложными задачами. Тем не менее, можно будет не использовать такие сложные алгоритмы
* push2, pop2: быстрее будем класть и брать со стека. Тут можно посмотреть на LDP/STP инструкции из Arm о которых я писал.
* 3 argument instructions. Инструкции будут иметь 3 аргумента, теперь на надо будет складывать память с регистром и мувать в другой регистр, можно сразу класть в другой. Тоже Arm так делает давным давно. Тут скорее всего лучше будет компиляторам такие инструкции производить.
* Conditional stores/loads -- это интересно, на Arm такого нет, я думаю хорошо взлетит во всяких compression/sorting
* Унификация SIMD. Intel тоже хочет быть похожим на SVE в Армах с непостоянной максимальной шириной максимального регистра. Поэтому вы можете спросить какая версия SIMD доступна и кодировать инструкции с помощью EVEX префикса. И даже не придётся компилировать 2 копии.
2. Тут нашли удивительную уязвимость с помощью фаззинга AMD процессоров https://lock.cmpxchg8b.com/zenbleed.html: более менее мои Ctrl+C Ctrl+V оно нашло за несколько секунд эксплуатации. Идея: давайте на разных процессорах запустим случайно сгенерированные программы, а также программы, где между инструкциями стоят lfence/mfence инструкции. Последние убивают instruction level parallelism, а значит и много оптимизаций вроде переименования регистров, speculative execution. После этого можно сверить все результаты. Если убрать все инструкции результат которых не определён, скажем, bsf (Bit Scan Forward при нуле не определён), то можно честно сравнивать выходы. Так и нашли этот баг. Удивительно крутая история.
3. Переписал Iguana Compression c Go на C++ (выложим код скорее всего). Получилось попробовать в более боевых условиях. Результаты хорошие, но не 2-3x заявленных в бенчмарках. По уровню сжатия версия без энтропийного кодирования чуть получше стандартного LZ4, а энтропийное где-то между ZSTD:2-3. В целом ничего не мешает увеличивать сжатие, так как алгоритм из LZ семейства и все алгоритмы сжатия из ZSTD можно перенести в Iguana. На Icelake писали, что где-то прибавка в 20-30%, а от себя скажу, что на Zen4 примерно всё ровно с ZSTD. AVX вещи у AMD всегда отстают на где-то 1 поколение от Intel. Догонят :)
Intel
Introducing Intel® Advanced Performance Extensions (Intel® APX)
These extensions expand the x86 instruction set with access to registers and features that improve performance.
Delayed decision
Хочется рассказать об оптимизациях, которые откладывают выигрыши до лучших времён. Их можно увидеть во многих системах, например, можно посчитать какую-то статистику или выучить, как правильно располагать код. Сложность таких оптимизаций в том, что они должны "продать" будущее. Какого-то единого формата как их находить особо нет, кроме как приближать данные о прошлом в настоящее. Если бы кто-то умел это делать лучше остальных идеально, у нас бы не было HFT и подобных компаний, занимающихся этим 24/7.
Такие вещи происходят от самых верхнеуровневых решений до самых низких. Приведём пример верхнеуровневого решения: кеширование.
Вы никогда не знаете, какой элемент лучше всего вытащить при переполнении кеша. Точнее знаете -- тот, который будет позже всех использован в будущем. Такой кеш называют идеальным кешом. Против него хорошо можно бенчмаркать прошлые данные, надеясь, что новые будут очень похожими. На фоне всяких идеальных кешей начали возникать теории и полным полно стратегий вытеснения -- LRU, WLRU, GDSF, ARC. Все со своими интересностями, а насколько мы может приблизиться к оптимальному результату. Оптимальный результат называют OPT, Belady's и тд, если вдруг хочется посмотреть. Что делать? В целом бенчмаркать, особо больше ничего, можно динамически что-то считать, можно машинное обучение пытаться применить. Единственное -- решение должно приниматься достаточно быстро, это же кеши. Плюс в том, что вы знаете верхнюю границу идеального результата. Старайтесь подумать, а что такое идеально тут.
Более локальные решения происходят даже на уровне аллокаторов. В TCMalloc при аллокации блока мы префетчим ещё 1 блок того же размера, потому что это лучше продаёт будущее несмотря на то, что ухудшает микробенчмарки. С одной стороны это достаточно просто и не так уж и дорого, но с другой, чтобы такое найти, надо было потратить годы и подробно смотреть на паттерны доступа.
Здесь особо нет правил, есть стратегии как можно находить такие моменты, одна из основных -- пробовать сотни различных конфигураций. Потратьте время, чтобы эти конфигурации было легко проверять. Удивительные результаты моей карьеры я не особо понимаю глубинно, лишь интуитивно методом проб и ошибок. Это что-то достаточно уникальное для software как мне кажется. Как слишком много всего, что работает.
Хочется рассказать об оптимизациях, которые откладывают выигрыши до лучших времён. Их можно увидеть во многих системах, например, можно посчитать какую-то статистику или выучить, как правильно располагать код. Сложность таких оптимизаций в том, что они должны "продать" будущее. Какого-то единого формата как их находить особо нет, кроме как приближать данные о прошлом в настоящее. Если бы кто-то умел это делать лучше остальных идеально, у нас бы не было HFT и подобных компаний, занимающихся этим 24/7.
Такие вещи происходят от самых верхнеуровневых решений до самых низких. Приведём пример верхнеуровневого решения: кеширование.
Вы никогда не знаете, какой элемент лучше всего вытащить при переполнении кеша. Точнее знаете -- тот, который будет позже всех использован в будущем. Такой кеш называют идеальным кешом. Против него хорошо можно бенчмаркать прошлые данные, надеясь, что новые будут очень похожими. На фоне всяких идеальных кешей начали возникать теории и полным полно стратегий вытеснения -- LRU, WLRU, GDSF, ARC. Все со своими интересностями, а насколько мы может приблизиться к оптимальному результату. Оптимальный результат называют OPT, Belady's и тд, если вдруг хочется посмотреть. Что делать? В целом бенчмаркать, особо больше ничего, можно динамически что-то считать, можно машинное обучение пытаться применить. Единственное -- решение должно приниматься достаточно быстро, это же кеши. Плюс в том, что вы знаете верхнюю границу идеального результата. Старайтесь подумать, а что такое идеально тут.
Более локальные решения происходят даже на уровне аллокаторов. В TCMalloc при аллокации блока мы префетчим ещё 1 блок того же размера, потому что это лучше продаёт будущее несмотря на то, что ухудшает микробенчмарки. С одной стороны это достаточно просто и не так уж и дорого, но с другой, чтобы такое найти, надо было потратить годы и подробно смотреть на паттерны доступа.
Здесь особо нет правил, есть стратегии как можно находить такие моменты, одна из основных -- пробовать сотни различных конфигураций. Потратьте время, чтобы эти конфигурации было легко проверять. Удивительные результаты моей карьеры я не особо понимаю глубинно, лишь интуитивно методом проб и ошибок. Это что-то достаточно уникальное для software как мне кажется. Как слишком много всего, что работает.
GitHub
tcmalloc/tcmalloc/internal/percpu_tcmalloc.h at 4c2f6e64e5345dc004023f2202ab358299634a0e · google/tcmalloc
Contribute to google/tcmalloc development by creating an account on GitHub.
Всё было сложно сесть и написать
Сейчас в солнечной калифорнии на саммите перформанса в гугле и было сложно сесть и написать что-то.
Unrelated to everything, хочется поговорить о уязвимостях как Inception и Zenbleed. Уязвимости интересные просто для чтения, но TL;DR мы сейчас можем читать где-то по 40-50 байт других процессов в секунду и хоть это не близко до того, чтобы эксплуатировать такие вещи на больших пространствах, забавно, что мы начинаем понимать как ломать branch predictors таким образом, чтобы красть какие-то данные.
Всё это имеет плохие последствия, если кто-то научится это эксплуатировать, также кажется, что ещё немного рисёрча и возможно уже не получится решить эту проблему всякими chicken bits в процессоре, а придётся делать настоящий pipeline flush в branch predictors. Всё это очень серьёзно скажется на перфе процессоров. Скорее всего 20-30% в среднем.
Моё мнение после общения с людьми из индустрии, что мы слишком далеко ушли в сложности в branch predictors и порой это сказывается на сложности дизайна. Самая большая проблема в этом, как ни странно, -- люди. Люди пишут код для него, плохо проверяют на безопасность, потому что рисёрч только сейчас начинает понимать, что мы написали.
Очень смешное решение предложил мой коллега -- запретить людям писать branch predictors на следующие несколько лет. С чем в целом, я согласен. Потому что формальные методы ещё не сильно выросли в этой науке, а кажется люди уже не справляются понять всеобщую картину.
Как с помощью software улучшить ситуацию? Собирать с profile guided optimizations, они хорошо унесут холодные блоки в даль от видимости. Проблема в том, что profile guided optimization (PGO) для людей как-то не сильно будет популярно, максимум так могут сделать компании. Тем не менее, такой подход простит несовершенство branch predictor, который, например, для Arm процессоров похуже, но становится неплохим, когда бинарный файл скомпилирован с PGO.
Надо как-то убирать людей из дизайна безопасносности в таких системах, люди должны только описывать, что значит безопасность и дальше использовать методы верификации. Я лично вижу в этом повторение истории с Pentium FDIV. Возможно в параллельной вселенной я бы этим позанимался.
Сейчас в солнечной калифорнии на саммите перформанса в гугле и было сложно сесть и написать что-то.
Unrelated to everything, хочется поговорить о уязвимостях как Inception и Zenbleed. Уязвимости интересные просто для чтения, но TL;DR мы сейчас можем читать где-то по 40-50 байт других процессов в секунду и хоть это не близко до того, чтобы эксплуатировать такие вещи на больших пространствах, забавно, что мы начинаем понимать как ломать branch predictors таким образом, чтобы красть какие-то данные.
Всё это имеет плохие последствия, если кто-то научится это эксплуатировать, также кажется, что ещё немного рисёрча и возможно уже не получится решить эту проблему всякими chicken bits в процессоре, а придётся делать настоящий pipeline flush в branch predictors. Всё это очень серьёзно скажется на перфе процессоров. Скорее всего 20-30% в среднем.
Моё мнение после общения с людьми из индустрии, что мы слишком далеко ушли в сложности в branch predictors и порой это сказывается на сложности дизайна. Самая большая проблема в этом, как ни странно, -- люди. Люди пишут код для него, плохо проверяют на безопасность, потому что рисёрч только сейчас начинает понимать, что мы написали.
Очень смешное решение предложил мой коллега -- запретить людям писать branch predictors на следующие несколько лет. С чем в целом, я согласен. Потому что формальные методы ещё не сильно выросли в этой науке, а кажется люди уже не справляются понять всеобщую картину.
Как с помощью software улучшить ситуацию? Собирать с profile guided optimizations, они хорошо унесут холодные блоки в даль от видимости. Проблема в том, что profile guided optimization (PGO) для людей как-то не сильно будет популярно, максимум так могут сделать компании. Тем не менее, такой подход простит несовершенство branch predictor, который, например, для Arm процессоров похуже, но становится неплохим, когда бинарный файл скомпилирован с PGO.
Надо как-то убирать людей из дизайна безопасносности в таких системах, люди должны только описывать, что значит безопасность и дальше использовать методы верификации. Я лично вижу в этом повторение истории с Pentium FDIV. Возможно в параллельной вселенной я бы этим позанимался.
CppCon
Прошёл тут на CppCon с докладом про сортировки https://sched.co/1Qtft Проекту два с лишним года и пора сделать очень красивое завершение.
Аж сам Herb Sutter записался.
Не хочу зарекаться, но я очень хочу верить, что это в последний раз, когда я выступаю на конференции по C++. В следующий раз надо как-нибудь на RustConf пройти, или вообще что-нибудь другое читать. В дальнейшем всё, что мне хочется делать с языками, это тихонечко помогать людям писать нормальный код.
Я тут ещё зачитался про S3-FIFO cache eviction policy. https://blog.jasony.me/system/cache/2023/08/01/s3fifo Достаточно интересная идея, что во многих кешах существует много элементов, которые лишь единожды вставлены и мало стратегий, которые их пытаются вытеснить достаточно рано. Авторы обещают улучшенные hit rates и перформанс. Достаточно красиво и элегантно. Съевши собаку в кешах, скажу, что одна из проблем, которую почти никто не исследует это проблема, сколько промах займёт на бекендах, поэтому в каких-нибудь базах данных любят делать Weighted стратегии (условные бОльшие записи сложнее достать с бекендов). Здесь не очень понятно как это делать, и автор отговаривается:
"Unless otherwise mentioned, we ignore object size because most production systems use slab storage for memory management, for which evictions are performed within the same slab class (objects of similar sizes)."
Дьявол в деталях, по мне это небольшая халтура для такого эксперта как он :). В проде в больших кешах никто не делает кеши по slab size, это слишком затратно и сложно настраиваемо, все используют Weighted стратегии.
Прошёл тут на CppCon с докладом про сортировки https://sched.co/1Qtft Проекту два с лишним года и пора сделать очень красивое завершение.
Аж сам Herb Sutter записался.
Не хочу зарекаться, но я очень хочу верить, что это в последний раз, когда я выступаю на конференции по C++. В следующий раз надо как-нибудь на RustConf пройти, или вообще что-нибудь другое читать. В дальнейшем всё, что мне хочется делать с языками, это тихонечко помогать людям писать нормальный код.
Я тут ещё зачитался про S3-FIFO cache eviction policy. https://blog.jasony.me/system/cache/2023/08/01/s3fifo Достаточно интересная идея, что во многих кешах существует много элементов, которые лишь единожды вставлены и мало стратегий, которые их пытаются вытеснить достаточно рано. Авторы обещают улучшенные hit rates и перформанс. Достаточно красиво и элегантно. Съевши собаку в кешах, скажу, что одна из проблем, которую почти никто не исследует это проблема, сколько промах займёт на бекендах, поэтому в каких-нибудь базах данных любят делать Weighted стратегии (условные бОльшие записи сложнее достать с бекендов). Здесь не очень понятно как это делать, и автор отговаривается:
"Unless otherwise mentioned, we ignore object size because most production systems use slab storage for memory management, for which evictions are performed within the same slab class (objects of similar sizes)."
Дьявол в деталях, по мне это небольшая халтура для такого эксперта как он :). В проде в больших кешах никто не делает кеши по slab size, это слишком затратно и сложно настраиваемо, все используют Weighted стратегии.
Sched
CppCon 2023: A Long Journey of Changing std::sort Imp...
View more about this event at CppCon 2023
Resources
BurntSushi обновил ripgrep для Arm пользуясь моими советами и статьёй, что не может не радовать, теперь с его слов "In short, simple ripgrep searches (likely the most common kind) get about twice as fast on Apple silicon now."
Unrelated.
Одна из больших ошибок в науке перформанса, на которую я обращал не так много внимания в первые годы, хотя стоило бы -- утилизация. Я очень люблю разворачивать циклы, улучшать алгоритмы и тд, но микробенчмарки врут, они однопоточные часто, и они часто всю память кладут в L1/L2 кеш и получается очень красиво, мол, вот вам, соптимизировали. Сейчас я даже не говорю про промахи мимо кешей, а именно как хорошо вы можете утилизировать все ресурсы. Memory bandwidth между памятью и ядрами сейчас упирается в 30-100 GB/s и не растёт. После этого порога -- тьма. Это означает, что в целом бесполезно оптимизировать хоть что угодно -- вы упёрлись в память. На одном процессе с десяток потоков не так страшно, но когда вы пытаетесь загрузить машинку и каждый процесс кушает свои 5-10GB/s, то в целом вы никогда не увидите, что упираетесь в CPU. И опять же, бесполезно оптимизировать хоть что-то на микробенчмарках, вы упираетесь в потолок, потому что просто хотите прочитать или записать память.
Какие ошибки и эффекты у этого всего?
1. "Ух, сейчас распараллелим на 32 потока и заживём". Нет, упрётесь в потолок и будете как на однопоточном.
2. "Что-то на одной машинке 99th latency совершенно другая, хотя вроде железо одно и то же". Да, потому что соседи могут всё скушать, а вам не особо что останется.
3. "Сэкономили 10% CPU, сейчас закажем машинок поменьше". Увы, не всегда
4. "Давайте кешик побольше сделаем и заживём". Опять же, шаг влево и бекенды будут отвечать в разы хуже
На практике пока не всё так плохо, но процессоры всё ещё развиваются, а скорость передачи данных всё, у неё истек закон Мура. 3-5% в год?
Что делать?
Уменьшайте работу с памятью. Трогайте горячие данные чаще, холодные реже. Собирайте кеш промахи (LLC-load-misses в perf stat) профайлом и уменьшайте их (хотя это может помочь *соседу* на машине, а не вам). Меньше алгоритмов на табличках, больше на CPU, меньше данных в целом. Удаляйте данные, перекладывайте меньше jsonов в конце концов, а.
BurntSushi обновил ripgrep для Arm пользуясь моими советами и статьёй, что не может не радовать, теперь с его слов "In short, simple ripgrep searches (likely the most common kind) get about twice as fast on Apple silicon now."
Unrelated.
Одна из больших ошибок в науке перформанса, на которую я обращал не так много внимания в первые годы, хотя стоило бы -- утилизация. Я очень люблю разворачивать циклы, улучшать алгоритмы и тд, но микробенчмарки врут, они однопоточные часто, и они часто всю память кладут в L1/L2 кеш и получается очень красиво, мол, вот вам, соптимизировали. Сейчас я даже не говорю про промахи мимо кешей, а именно как хорошо вы можете утилизировать все ресурсы. Memory bandwidth между памятью и ядрами сейчас упирается в 30-100 GB/s и не растёт. После этого порога -- тьма. Это означает, что в целом бесполезно оптимизировать хоть что угодно -- вы упёрлись в память. На одном процессе с десяток потоков не так страшно, но когда вы пытаетесь загрузить машинку и каждый процесс кушает свои 5-10GB/s, то в целом вы никогда не увидите, что упираетесь в CPU. И опять же, бесполезно оптимизировать хоть что-то на микробенчмарках, вы упираетесь в потолок, потому что просто хотите прочитать или записать память.
Какие ошибки и эффекты у этого всего?
1. "Ух, сейчас распараллелим на 32 потока и заживём". Нет, упрётесь в потолок и будете как на однопоточном.
2. "Что-то на одной машинке 99th latency совершенно другая, хотя вроде железо одно и то же". Да, потому что соседи могут всё скушать, а вам не особо что останется.
3. "Сэкономили 10% CPU, сейчас закажем машинок поменьше". Увы, не всегда
4. "Давайте кешик побольше сделаем и заживём". Опять же, шаг влево и бекенды будут отвечать в разы хуже
На практике пока не всё так плохо, но процессоры всё ещё развиваются, а скорость передачи данных всё, у неё истек закон Мура. 3-5% в год?
Что делать?
Уменьшайте работу с памятью. Трогайте горячие данные чаще, холодные реже. Собирайте кеш промахи (LLC-load-misses в perf stat) профайлом и уменьшайте их (хотя это может помочь *соседу* на машине, а не вам). Меньше алгоритмов на табличках, больше на CPU, меньше данных в целом. Удаляйте данные, перекладывайте меньше jsonов в конце концов, а.
GitHub
add aarch64 SIMD implementations of memchr and memmem (and other goodies) by BurntSushi · Pull Request #129 · BurntSushi/memchr
This PR doesn't just add aarch64-specific code, but it refactors pretty much everything about how the code is organized. There are big perf wins for aarch64 (see benchmark results below), and a...
Фаззили 5 лет и не нашли
Читал тут как в дереве хаффмана нашли out of bounds read в libwebp https://blog.isosceles.com/the-webp-0day/. Есть спекуляции, что этим пользуется Pegasus.
И снова вопрос, почему фаззинг не нашёл такой пример за 5 лет фаззинга?
Не похоже ли это на https://www.tg-me.com/experimentalchill/238, когда мы в ZSTD нашли битые данные хотя фаззили достаточно несложный код 2 года?
Как я уже писал, в фаззинге нет магии, он просто пытается зайти во все строки кода, и совсем странные крайние случаи вряд ли найдёт. Всем советую прочитать анализ, а так если кто-то хочет написать сложный проект по тому, как находить такие баги чаще, то welcome. Это в самом деле a million dollar question, без преувеличения.
Читал тут как в дереве хаффмана нашли out of bounds read в libwebp https://blog.isosceles.com/the-webp-0day/. Есть спекуляции, что этим пользуется Pegasus.
И снова вопрос, почему фаззинг не нашёл такой пример за 5 лет фаззинга?
Не похоже ли это на https://www.tg-me.com/experimentalchill/238, когда мы в ZSTD нашли битые данные хотя фаззили достаточно несложный код 2 года?
Как я уже писал, в фаззинге нет магии, он просто пытается зайти во все строки кода, и совсем странные крайние случаи вряд ли найдёт. Всем советую прочитать анализ, а так если кто-то хочет написать сложный проект по тому, как находить такие баги чаще, то welcome. Это в самом деле a million dollar question, без преувеличения.
Isosceles Blog
The WebP 0day
Early last week, Google released a new stable update for Chrome. The update included a single security fix that was reported by Apple's Security Engineering and Architecture (SEAR) team. The issue, CVE-2023-4863, was a heap buffer overflow in the WebP image…
Тут вышла OOPSLA -- хорошая конференция по языкам программирования. Зацепило 3 статьи
Randomized Testing of Byzantine Fault Tolerant Algorithms
https://dl.acm.org/doi/10.1145/3586053
Блокчейн всё таки принёс пользу -- теперь у нас есть какое-то количество византийских протоколов (то есть распределённых протоколов, которые сохраняют свойства, но могут портить сообщения и тд), и рисёрчеры подтянулись и придумали интересное тестирование -- давайте тестировать не только случайные падения и случайные изменения, а опишем структурно как в фаззинге что можно менять. Из неочевидных свойств -- пишут, что именно сок в мелких изменениях, так обнаружилось больше багов.
ByzzFuzz detected several bugs in the implementation of PBFT, a potential liveness violation in Tendermint, and materialized two theoretically described vulnerabilities in Ripple’s XRP Ledger Consensus Algorithm. Moreover, we discovered a previously unknown fault-tolerance bug in the production implementation of Ripple, which is confirmed by the developers and fixed.
Fat Pointers for Temporal Memory Safety of C
https://dl.acm.org/doi/10.1145/3586038
Давным давно уже пытаются создать язык Checked-C, который проверяет, всё ли хорошо с разыменовываниями указателей. Только Checked-C работает только с массивами, мол, не выходите за границы. А вот сохранения протухших указателей намного сложнее найти -- надо уже смотреть за лайфтаймами. Статья придумала жирные указатели, которые аннотируются метаданными, куда ушёл указатель и будет ли он изменён потом. Лёгкое дополнение к трансформации компиляции. 2/3 статьи о том, как это сделать быстро с ассемблером и тд. Пишут о 1-2k строк в день миграции. На самом деле вывод более интересный -- если тесты неплохие, то в целом вы получите код с unique ownership, а значит его и легче будет переписать/протранслировать на Rust уже какой-нибудь автоматической тулзой. То есть не сразу писать C -> Rust, а C -> Checked-C -> Rust, что будет менее болезненно и потенциально последняя часть автоматически.
Accelerating Fuzzing through Prefix-Guided Execution
https://dl.acm.org/doi/10.1145/3586027
Одна из проблем фаззинга, как было видно в прошлых постах, скорость с которой он сходится для багов. Иногда не находит годами. Если его ускорить раза в два, может быть будут шансы найти что-то побыстрее. В статье рассказывают как фаззинг устроен в целом, мол, если вход не увеличил покрытие, то он выбрасывается. Авторы увидели интересную особенность, что покрытие улучшается практически всегда, когда тест доходит до определенного префикса выполнения -- то есть если код веток на 20, то достаточно часто посмотреть на первые 5 и если мы пришли в те же 5 веток, то можно просто не исполнять вход дальше. Особенность интересная, но весьма теоретическая -- это число "5" для всех разное и найти его -- надо потратить какое-то время на нахождение его бинарным поиском. Но абсолютно магическим фактом кажется, что это число почему-то существует на всём, на чём потестили и достигает того же покрытия, что и обычный фаззер за 3-10x меньше времени. Прямо применить будет слегка сложно, но рисёрч продолжается. Вообще Zhendong Su -- какая-то звезда современного рисёрча фаззинга, у него по несколько статей в год.
Randomized Testing of Byzantine Fault Tolerant Algorithms
https://dl.acm.org/doi/10.1145/3586053
Блокчейн всё таки принёс пользу -- теперь у нас есть какое-то количество византийских протоколов (то есть распределённых протоколов, которые сохраняют свойства, но могут портить сообщения и тд), и рисёрчеры подтянулись и придумали интересное тестирование -- давайте тестировать не только случайные падения и случайные изменения, а опишем структурно как в фаззинге что можно менять. Из неочевидных свойств -- пишут, что именно сок в мелких изменениях, так обнаружилось больше багов.
ByzzFuzz detected several bugs in the implementation of PBFT, a potential liveness violation in Tendermint, and materialized two theoretically described vulnerabilities in Ripple’s XRP Ledger Consensus Algorithm. Moreover, we discovered a previously unknown fault-tolerance bug in the production implementation of Ripple, which is confirmed by the developers and fixed.
Fat Pointers for Temporal Memory Safety of C
https://dl.acm.org/doi/10.1145/3586038
Давным давно уже пытаются создать язык Checked-C, который проверяет, всё ли хорошо с разыменовываниями указателей. Только Checked-C работает только с массивами, мол, не выходите за границы. А вот сохранения протухших указателей намного сложнее найти -- надо уже смотреть за лайфтаймами. Статья придумала жирные указатели, которые аннотируются метаданными, куда ушёл указатель и будет ли он изменён потом. Лёгкое дополнение к трансформации компиляции. 2/3 статьи о том, как это сделать быстро с ассемблером и тд. Пишут о 1-2k строк в день миграции. На самом деле вывод более интересный -- если тесты неплохие, то в целом вы получите код с unique ownership, а значит его и легче будет переписать/протранслировать на Rust уже какой-нибудь автоматической тулзой. То есть не сразу писать C -> Rust, а C -> Checked-C -> Rust, что будет менее болезненно и потенциально последняя часть автоматически.
Accelerating Fuzzing through Prefix-Guided Execution
https://dl.acm.org/doi/10.1145/3586027
Одна из проблем фаззинга, как было видно в прошлых постах, скорость с которой он сходится для багов. Иногда не находит годами. Если его ускорить раза в два, может быть будут шансы найти что-то побыстрее. В статье рассказывают как фаззинг устроен в целом, мол, если вход не увеличил покрытие, то он выбрасывается. Авторы увидели интересную особенность, что покрытие улучшается практически всегда, когда тест доходит до определенного префикса выполнения -- то есть если код веток на 20, то достаточно часто посмотреть на первые 5 и если мы пришли в те же 5 веток, то можно просто не исполнять вход дальше. Особенность интересная, но весьма теоретическая -- это число "5" для всех разное и найти его -- надо потратить какое-то время на нахождение его бинарным поиском. Но абсолютно магическим фактом кажется, что это число почему-то существует на всём, на чём потестили и достигает того же покрытия, что и обычный фаззер за 3-10x меньше времени. Прямо применить будет слегка сложно, но рисёрч продолжается. Вообще Zhendong Su -- какая-то звезда современного рисёрча фаззинга, у него по несколько статей в год.
2023.splashcon.org
SPLASH 2023 - OOPSLA - SPLASH 2023
The ACM SIGPLAN International Conference on Systems, Programming, Languages and Applications: Software for Humanity (SPLASH) embraces all aspects of software construction and delivery, to make it the premier conference on the applications of programming languages…
Contest
Пописал тут контест от телеграма https://www.tg-me.com/contest/330. В целом задача -- всё, что написано в markup code в телеге -- определить, код ли это и какого языка программирования из 100 самых популярных. Как обычно, ни данных, ни условий тестирования не дали, классический телеграм.
Из уже существующий решений есть
* VSCode'овский https://github.com/yoeo/guesslang, прилагающий к нему https://github.com/yoeo/guesslangtools. Из хорошего, 55 языков поддерживает c 90%+ качеством, из плохого работает на больших кусках кода.
* Какая-то статья https://github.com/aliostad/deep-learning-lang-detection, утверждают 99%+ качества. Обучение у них на слишком хороших данных.
* Какие-то регексовские вещи, которые очевидно никогда не наберут 90%+ качества
Вторую я покрутил, повертел и получил тоже ~90% качества на средненьких таких данных.
Как брать данные
* https://libraries.io/
* https://huggingface.co/datasets/bigcode/the-stack
* https://sourcegraph.com/search -> Actions -> Export Results
В эпоху LLM, на hugging face собрали неплохой датасет, который я почему-то нашёл только в последние пару дней, чутка оттуда покачал, но особо много это не дало, а кластера для терабайтов у меня не было. В итоге использовал libraries.io и sourcegraph.
Собрал я 5M файлов, качал я это дело на 5TB машинке пару дней, github отдавал разным машинам в районе 50-100MB/s в зависимости проснулись ли прогеры из Сан Франциско :), много тротлили, но я их переиграл. Суммарно после дедупликации получилось 100GB кода.
Дальше начинается самое веселое. Из хорошего в файлах гитхаба есть расширение, которое почти однозначно определяет язык. Ага, конечно,
.hh -> C++ и Hack
.p -> Gnuplot и OpenEdgeABL
.fs -> Forth и F#
У гитхаба есть неплохой алгоритм детекции таких disputes: heuristics.yml на регулярных выражениях. Целый день я сидел и фильтровал данные, помимо добавлял языки программирования, которых Github не знает.
Например, FunC и FIFT. В целом видна человеческая рука здесь по добавлению 2 языков программирования от уважаемого Николая. По всему интернету файлов существует в районе 1000 и спасибо sourcegraph, который смог это всё найти. Но мусора в расширениях .fc и .fift было предостаточно, выфильтровали :)
Далее обучение. Я взял guesslang и чутка крутил batchsize, hidden dnn layers и получил в среднем 88% аккуратности и 92.5% на топ50 самых популярных языках
В конце начинается самое интересное, а собственно, как достать markup comments телеграма? К сожалению, датасетов почти нет, tgstat.ru не сохраняет информацию об этом. Я покачал каких-то прогерских community и докручивал на маленьких данных.
Самое плохое, что лучше всего сработало при маленьких кусках кода просто повторить его, пока не станет 30-40 строк кода. От таких положений вещей модель начинала сходиться к правильным ответам чаще.
Ещё, конечно, не забывайте, что любой текст это Markdown, поэтому чтобы различить одно от другого надо постараться. Докручивали уже disputes какими-то регекспами из heuristics.yml и логикой, мол, если строк мало, то вряд ли это эзотерический язык. Всё равно люди компилируют C код как C++, а Hack почти в природе нет, кроме как у Meta.
Самое сложное в конце было найти баланс, а что в общем-то авторы, хотят, что они вообще будут тестировать и какое распределение у всего и вся. Скорее всего будет 95% не языков программирования.
Последние полдня потратил на то, чтобы собрать tensorflow C++ с нуля и вкомпилить в .so, потому что принимать ничего другого они не хотят. Ну что ж, значит tensorflow. Скомпилировал. Отправил 600Mb .so файл.
Зато более менее работает, 2-3 ms на 4kb.
Делал я это дело 7-8 дней, из которых 3 полных часов по 10, ещё часа по 3-4 вечерами в оставшиеся дни.
Что будет, то будет. Ради фана и узнать что-то новое делал. Узнал о многих языках программирования вроде Raku, ICON, Keyman, LOGO.
И получайте почти красивую картинку confusion matrix.
Пописал тут контест от телеграма https://www.tg-me.com/contest/330. В целом задача -- всё, что написано в markup code в телеге -- определить, код ли это и какого языка программирования из 100 самых популярных. Как обычно, ни данных, ни условий тестирования не дали, классический телеграм.
Из уже существующий решений есть
* VSCode'овский https://github.com/yoeo/guesslang, прилагающий к нему https://github.com/yoeo/guesslangtools. Из хорошего, 55 языков поддерживает c 90%+ качеством, из плохого работает на больших кусках кода.
* Какая-то статья https://github.com/aliostad/deep-learning-lang-detection, утверждают 99%+ качества. Обучение у них на слишком хороших данных.
* Какие-то регексовские вещи, которые очевидно никогда не наберут 90%+ качества
Вторую я покрутил, повертел и получил тоже ~90% качества на средненьких таких данных.
Как брать данные
* https://libraries.io/
* https://huggingface.co/datasets/bigcode/the-stack
* https://sourcegraph.com/search -> Actions -> Export Results
В эпоху LLM, на hugging face собрали неплохой датасет, который я почему-то нашёл только в последние пару дней, чутка оттуда покачал, но особо много это не дало, а кластера для терабайтов у меня не было. В итоге использовал libraries.io и sourcegraph.
Собрал я 5M файлов, качал я это дело на 5TB машинке пару дней, github отдавал разным машинам в районе 50-100MB/s в зависимости проснулись ли прогеры из Сан Франциско :), много тротлили, но я их переиграл. Суммарно после дедупликации получилось 100GB кода.
Дальше начинается самое веселое. Из хорошего в файлах гитхаба есть расширение, которое почти однозначно определяет язык. Ага, конечно,
.hh -> C++ и Hack
.p -> Gnuplot и OpenEdgeABL
.fs -> Forth и F#
У гитхаба есть неплохой алгоритм детекции таких disputes: heuristics.yml на регулярных выражениях. Целый день я сидел и фильтровал данные, помимо добавлял языки программирования, которых Github не знает.
Например, FunC и FIFT. В целом видна человеческая рука здесь по добавлению 2 языков программирования от уважаемого Николая. По всему интернету файлов существует в районе 1000 и спасибо sourcegraph, который смог это всё найти. Но мусора в расширениях .fc и .fift было предостаточно, выфильтровали :)
Далее обучение. Я взял guesslang и чутка крутил batchsize, hidden dnn layers и получил в среднем 88% аккуратности и 92.5% на топ50 самых популярных языках
В конце начинается самое интересное, а собственно, как достать markup comments телеграма? К сожалению, датасетов почти нет, tgstat.ru не сохраняет информацию об этом. Я покачал каких-то прогерских community и докручивал на маленьких данных.
Самое плохое, что лучше всего сработало при маленьких кусках кода просто повторить его, пока не станет 30-40 строк кода. От таких положений вещей модель начинала сходиться к правильным ответам чаще.
Ещё, конечно, не забывайте, что любой текст это Markdown, поэтому чтобы различить одно от другого надо постараться. Докручивали уже disputes какими-то регекспами из heuristics.yml и логикой, мол, если строк мало, то вряд ли это эзотерический язык. Всё равно люди компилируют C код как C++, а Hack почти в природе нет, кроме как у Meta.
Самое сложное в конце было найти баланс, а что в общем-то авторы, хотят, что они вообще будут тестировать и какое распределение у всего и вся. Скорее всего будет 95% не языков программирования.
Последние полдня потратил на то, чтобы собрать tensorflow C++ с нуля и вкомпилить в .so, потому что принимать ничего другого они не хотят. Ну что ж, значит tensorflow. Скомпилировал. Отправил 600Mb .so файл.
Зато более менее работает, 2-3 ms на 4kb.
Делал я это дело 7-8 дней, из которых 3 полных часов по 10, ещё часа по 3-4 вечерами в оставшиеся дни.
Что будет, то будет. Ради фана и узнать что-то новое делал. Узнал о многих языках программирования вроде Raku, ICON, Keyman, LOGO.
И получайте почти красивую картинку confusion matrix.
Telegram
Telegram Contests
🏆 Telegram ML Competition
Prize fund: $40,000 – from which the 1st place winner will receive $15,000 if any submissions qualify for 1st place.
Deadline: 23:59 on October 15th (Dubai time)
Who can participate: Everyone
Results: October 29th, 2023
Telegram…
Prize fund: $40,000 – from which the 1st place winner will receive $15,000 if any submissions qualify for 1st place.
Deadline: 23:59 on October 15th (Dubai time)
Who can participate: Everyone
Results: October 29th, 2023
Telegram…
Давно я не писал, поэтому чуть больше в одном посте
1. Я тут сегодня нашёл интересное применение алгоритма поиска максимального потока, о котором вы вряд ли слышали -- для тестов. В GUnit есть функция UnorderedElementsAre(container), которая сравнивает, что два множества совпадают. Проблема в том, что для общего случая элементы могут быть несравнимы или нехешируемы, тогда проверять на равенство становится сложнее, так как запрещены сортировки и хеш-мапы. Задача решается с помощью максимального паросочетания: между двумя множествами строится граф равенства и после этого находится максимальное паросочетание. В GUnit код находится здесь. UPD. Также в комментариях подсказали, что такая абстракция удобна для того, чтобы иметь разные проверки, скажем, что в множестве есть элементы строго больше, чем в другом (пример).
2. В Google обновился C++ Style Guide на C++20. Ничего не написано про модули, ranges и корутины, потому что всё ещё нет консенсуса. В целом хорошее изменение, вещи прогрессируют.
3. Тут рассказано, сколько фаззеру надо было фаззить, чтобы найти известный сверху баг в WebRTC: https://www.srlabs.de/blog-post/advanced-fuzzing-unmasks-elusive-vulnerabilities . TL;DR 20 независимых байт, что достаточно много для фаззеров. В похожем баге в ZSTD, который мы нашли в марте, надо было около 7-8 специфичных байт после того как фаззер зашёл в нужную строчку кода. Чтобы найти баг в ZSTD и WebRTC нужны более интересные техники. Например, в ZSTD хватило бы добавлять 2^n рандомных байт в конец, а в WebRTC сделать большой if/switch на табличку в 256 байт. Кажется нужен стажёр
4. Google Pixel 8 имеет поддержку Arm MTE. Это фича, которая помечает аллокации специальными тегами на уровне битов указателей и проверяет на уровне железа, читаете ли вы валидную память. Надеюсь, мы найдем очень много всякого разного со временем.
5. Я выложил свою презентацию с CppCon про сортировки https://danlark1.github.io/cppcon_sort_2023 . Запись будет где-то в январе :)
1. Я тут сегодня нашёл интересное применение алгоритма поиска максимального потока, о котором вы вряд ли слышали -- для тестов. В GUnit есть функция UnorderedElementsAre(container), которая сравнивает, что два множества совпадают. Проблема в том, что для общего случая элементы могут быть несравнимы или нехешируемы, тогда проверять на равенство становится сложнее, так как запрещены сортировки и хеш-мапы. Задача решается с помощью максимального паросочетания: между двумя множествами строится граф равенства и после этого находится максимальное паросочетание. В GUnit код находится здесь. UPD. Также в комментариях подсказали, что такая абстракция удобна для того, чтобы иметь разные проверки, скажем, что в множестве есть элементы строго больше, чем в другом (пример).
2. В Google обновился C++ Style Guide на C++20. Ничего не написано про модули, ranges и корутины, потому что всё ещё нет консенсуса. В целом хорошее изменение, вещи прогрессируют.
3. Тут рассказано, сколько фаззеру надо было фаззить, чтобы найти известный сверху баг в WebRTC: https://www.srlabs.de/blog-post/advanced-fuzzing-unmasks-elusive-vulnerabilities . TL;DR 20 независимых байт, что достаточно много для фаззеров. В похожем баге в ZSTD, который мы нашли в марте, надо было около 7-8 специфичных байт после того как фаззер зашёл в нужную строчку кода. Чтобы найти баг в ZSTD и WebRTC нужны более интересные техники. Например, в ZSTD хватило бы добавлять 2^n рандомных байт в конец, а в WebRTC сделать большой if/switch на табличку в 256 байт. Кажется нужен стажёр
4. Google Pixel 8 имеет поддержку Arm MTE. Это фича, которая помечает аллокации специальными тегами на уровне битов указателей и проверяет на уровне железа, читаете ли вы валидную память. Надеюсь, мы найдем очень много всякого разного со временем.
5. Я выложил свою презентацию с CppCon про сортировки https://danlark1.github.io/cppcon_sort_2023 . Запись будет где-то в январе :)
Wikipedia
Задача о максимальном потоке
В теории оптимизации и теории графов, задача о максимальном потоке заключается в нахождении такого потока по транспортной сети, что сумма потоков из истока, или, что то же самое, сумма потоков в сток максимальна.
Тут была конференция SOSP . Запомнились некоторые интересные статьи
Silent Data Corruption in Alibaba Cloud. По сравнению со статьями от Google и Meta, Alibaba раскрыла больше данных о том насколько часто находятся битые процессоры и данные в проде. Из интересного, они увидели, что если процессор сильнее нагревается, то вероятность битой инструкции экспоненциально увеличивается. Из ещё забавного -- битфлипы очень часто происходят на тех же позициях в регистре при попытках воспроизвести. Из странного, большинство багов не находят в preprod. Из не понравившегося -- много странных графиков, экстраполяция по 27 битым процессорам и тд.
Project Silica: Towards Sustainable Cloud Archival Storage in Glass. Это, наверное, самая интересная статья, которую я прочитал. Суть в том, что хранение очень холодных данных на магнитных технологиях вроде HDD и тд дорогое, надо обновлять, так как диски вылетают и не остаются на десятки лет. Авторы рассказывают о 12-летнем опыте, как можно хранить данные в стекле, которое не ржавеет и не гниет, у которого срок службы >1k лет. Да, в стекле прям вырезают биты и в итоге данные хранятся дольше.
QuePaxa: Escaping the Tyranny of Timeouts in Consensus. В консенсусных алгоритмах вроде Raft и Paxos часто таймауты вызывают переизбрание лидера. При всяких DoS аттаках система встаёт и не очень продвигается. В итоге поставишь большой таймаут -- долго будет переизбрание, маленький -- слишком много. В QuePaxa предлагают делать хеджирование запросов, чтобы проверять liveness, модификацию Paxos, чтобы несколько proposers предлагали себя с весами в зависимости от быстроты ответов. Интересная и технически непростая статья.
A Cloud-Scale Characterization of Remote Procedure Calls. Статья от Google про 700 миллиардов RPC внутри нас, что мы выучили и куда стремимся с точки зрения оверхеда и Software. Смотрим на библиотеки, scheduling, cross DC traffic. Показываем,что в DC мы всё ещё millisecond-scale и до micro-second нам ещё далеко. Много времени в статье уделяется tail latency -- сколько занимает оверхед RPC framework на 99%. Один из авторов Soheil -- невероятно приятный человек, с кем работать одно удовольствие. Статья читается просто и даёт много инсайтов, что происходит внутри с сетью с точки зрения пользователя.
Silent Data Corruption in Alibaba Cloud. По сравнению со статьями от Google и Meta, Alibaba раскрыла больше данных о том насколько часто находятся битые процессоры и данные в проде. Из интересного, они увидели, что если процессор сильнее нагревается, то вероятность битой инструкции экспоненциально увеличивается. Из ещё забавного -- битфлипы очень часто происходят на тех же позициях в регистре при попытках воспроизвести. Из странного, большинство багов не находят в preprod. Из не понравившегося -- много странных графиков, экстраполяция по 27 битым процессорам и тд.
Project Silica: Towards Sustainable Cloud Archival Storage in Glass. Это, наверное, самая интересная статья, которую я прочитал. Суть в том, что хранение очень холодных данных на магнитных технологиях вроде HDD и тд дорогое, надо обновлять, так как диски вылетают и не остаются на десятки лет. Авторы рассказывают о 12-летнем опыте, как можно хранить данные в стекле, которое не ржавеет и не гниет, у которого срок службы >1k лет. Да, в стекле прям вырезают биты и в итоге данные хранятся дольше.
QuePaxa: Escaping the Tyranny of Timeouts in Consensus. В консенсусных алгоритмах вроде Raft и Paxos часто таймауты вызывают переизбрание лидера. При всяких DoS аттаках система встаёт и не очень продвигается. В итоге поставишь большой таймаут -- долго будет переизбрание, маленький -- слишком много. В QuePaxa предлагают делать хеджирование запросов, чтобы проверять liveness, модификацию Paxos, чтобы несколько proposers предлагали себя с весами в зависимости от быстроты ответов. Интересная и технически непростая статья.
A Cloud-Scale Characterization of Remote Procedure Calls. Статья от Google про 700 миллиардов RPC внутри нас, что мы выучили и куда стремимся с точки зрения оверхеда и Software. Смотрим на библиотеки, scheduling, cross DC traffic. Показываем,что в DC мы всё ещё millisecond-scale и до micro-second нам ещё далеко. Много времени в статье уделяется tail latency -- сколько занимает оверхед RPC framework на 99%. Один из авторов Soheil -- невероятно приятный человек, с кем работать одно удовольствие. Статья читается просто и даёт много инсайтов, что происходит внутри с сетью с точки зрения пользователя.
Тут вышла статья от Кости Серебряного -- человек, который себе карьеру сделал на ASAN, Fuzzing и тд про GWP-ASAN. Это своеобразная модификация аллокатора, которая не включает полную мощь ASAN в проде, так как будут проблемы с оверхедом, а создаёт отдельный буффер, где страницы памяти Linux чередуются -- то, из чего можно читать и то из чего нельзя (PROT_NONE), таким образом легче словить out of bounds ошибки. При аллокациях выдаётся память из одного региона, где можно читать, при деаллокации тоже помечается PROT_NONE, а при use after free, если повезёт, и попадёт туда, куда не надо, то найдёт ошибку в C/C++ подобном коде. Тем самым случайным образом аллокации попадают в буффер и имеют шансы обнаружить ошибки уже не в тестах, а в проде. Ну, мы знаем такие техники, и вероятности не очень большие поймать при одном запуске. Прелесть такого метода в том, что оверхед минимальный, меньше 0.2%
А теперь предположим у вас есть софт, который распространяется на тысячи, миллионы людей и девайсов. Google, Apple, Meta, приложения в Google/App Store, софт Windows, игра в Steam или просто серверы и их много, и работают они долго. Так получается, что вы теперь проверяете на корректность не какой-то участок кода при запуске, а размазываете ASAN на все аллокации на тысячах и миллионах устройств.
Сотни, тысячи багов, простая имплементация и красивая история. Ни тесты, ни фаззинг не спасают от багов в проде
А теперь предположим у вас есть софт, который распространяется на тысячи, миллионы людей и девайсов. Google, Apple, Meta, приложения в Google/App Store, софт Windows, игра в Steam или просто серверы и их много, и работают они долго. Так получается, что вы теперь проверяете на корректность не какой-то участок кода при запуске, а размазываете ASAN на все аллокации на тысячах и миллионах устройств.
Сотни, тысячи багов, простая имплементация и красивая история. Ни тесты, ни фаззинг не спасают от багов в проде
Тут выложили видео со мной на CppCon https://www.youtube.com/watch?v=cMRyQkrjEeI
Вроде даже неплохо получилось.
Конец года наступает, а посты всё реже и реже выходят. Я очень много занимался тем, о чём вообще нельзя рассказывать, какие-то внутренние штуки, о которых невозможно писать, не рассказав тучу контекста.
Технологический мир в последний год был сфокусирован вокруг LLM, а мне посредственно интересна эта тема. Можно поговорить про UltraFastBERT https://arxiv.org/pdf/2311.10770.pdf , где используют часть нейронов через дерево и с помощью sparse matrix multiplication достигают 78x быстрее inference, но, к сожалению, только на CPU. В целом потолок виден в уменьшении всего этого, но алгоритм не положить на GPU и это вопрос на следующий миллиард долларов. Удешевить всё дело в 80 раз даст доступ к LLM всем в каждом углу.
Я вообще редко читаю на постоянной основе чужие блоги, но блог Стивена Вольфрама мне особенно понравился в прошлом году. Например, про ChatGPT.
В целом проекты как https://github.com/tinygrad/tinygrad могут попытаться сделать что-то около крутого вокруг идеи m
Ещё из интересного я вошёл в комитет по разработке следующего поколения SIMD на Arm -- может быть назовём SVE 3 или как-то так. Учитывая, что SVE2 и SVE2.1 было фактически задизайнено Apple и не особо развивалось у них за ненадобностью, то может быть получится продвинуть дело подальше в правильном направлении.
Вроде даже неплохо получилось.
Конец года наступает, а посты всё реже и реже выходят. Я очень много занимался тем, о чём вообще нельзя рассказывать, какие-то внутренние штуки, о которых невозможно писать, не рассказав тучу контекста.
Технологический мир в последний год был сфокусирован вокруг LLM, а мне посредственно интересна эта тема. Можно поговорить про UltraFastBERT https://arxiv.org/pdf/2311.10770.pdf , где используют часть нейронов через дерево и с помощью sparse matrix multiplication достигают 78x быстрее inference, но, к сожалению, только на CPU. В целом потолок виден в уменьшении всего этого, но алгоритм не положить на GPU и это вопрос на следующий миллиард долларов. Удешевить всё дело в 80 раз даст доступ к LLM всем в каждом углу.
Я вообще редко читаю на постоянной основе чужие блоги, но блог Стивена Вольфрама мне особенно понравился в прошлом году. Например, про ChatGPT.
The basic answer, I think, is that language is at a fundamental level somehow simpler than it seems. And this means that ChatGPT is successfully able to “capture the essence” of human language and the thinking behind it. And moreover, in its training, ChatGPT has somehow “implicitly discovered” whatever regularities in language (and thinking) make this possible.
...
And perhaps there’s nothing to be said about how it can be done beyond “somehow it happens when you have 175 billion neural net weights”. But I strongly suspect that there’s a much simpler and stronger story.
В целом проекты как https://github.com/tinygrad/tinygrad могут попытаться сделать что-то около крутого вокруг идеи m
uch simpler and stronger story.
Пытаться утилизировать всё, в т. ч. на CPU имеет смысл и здравый риск. George Hotz, несмотря на всю его спорную личность, очень сильный инженер и, наверное, много в моем стиле программирования я взял от него. В универе смотрел его стримы чуть ли не 24/7 на старших курсах.Ещё из интересного я вошёл в комитет по разработке следующего поколения SIMD на Arm -- может быть назовём SVE 3 или как-то так. Учитывая, что SVE2 и SVE2.1 было фактически задизайнено Apple и не особо развивалось у них за ненадобностью, то может быть получится продвинуть дело подальше в правильном направлении.
YouTube
A Long Journey of Changing std::sort Implementation at Scale - Danila Kutenin - CppCon 2023
https://cppcon.org/
---
A Long Journey of Changing std::sort Implementation at Scale - Danila Kutenin - CppCon 2023
https://github.com/CppCon/CppCon2023
Sorting algorithms have been improving for almost 50 years now with claims of better efficiency and…
---
A Long Journey of Changing std::sort Implementation at Scale - Danila Kutenin - CppCon 2023
https://github.com/CppCon/CppCon2023
Sorting algorithms have been improving for almost 50 years now with claims of better efficiency and…
В C++20 завезли destroying delete operator. Его основное отличие от обычного delete -- что при удалении через destroying delete вы сначала заходите в функцию, где сами должны вызвать деструктор
https://gcc.godbolt.org/z/zqd8Yn7ff
Из интересного, вы теперь можете сами писать свои vtable: https://gcc.godbolt.org/z/EYMn1sd1o
Когда вы можете писать свои vtable, вы можете классы инициализировать через memset и тд, потому что вы убираете полную инициализацию.
Например, протобуфы. Все они наследуются от MessageLite, а теперь в кодогенерации можно свою vtable написать и инициализировать все поля быстро через mem*. До этого это было undefined behavior из-за нефиксированности имплементации vtable.
https://en.cppreference.com/w/cpp/memory/new/operator_delete. Cм 27-30.
struct Foo {
~Foo() {
std::cout << "In Bar::~Bar()\n";
}
void operator delete(Foo* p, std::destroying_delete_t) {
std::cout << "In Foo::operator delete(Foo*, std::destroying_delete_t)\n";
p->~Foo();
::operator delete(p);
}
};
https://gcc.godbolt.org/z/zqd8Yn7ff
Из интересного, вы теперь можете сами писать свои vtable: https://gcc.godbolt.org/z/EYMn1sd1o
struct Vehicle {
const enum Types { CAR, TRUCK } type;
Vehicle(Types t) : type(t) {}
void operator delete(Vehicle*, std::destroying_delete_t);
};
// Implement Car, Truck
void Vehicle::operator delete(Vehicle *p, std::destroying_delete_t) {
switch (p->type) {
case CAR:
static_cast<Car*>(p)->~Car();
break;
case TRUCK:
static_cast<Truck*>(p)->~Truck();
break;
}
::operator delete(p);
}
Когда вы можете писать свои vtable, вы можете классы инициализировать через memset и тд, потому что вы убираете полную инициализацию.
Например, протобуфы. Все они наследуются от MessageLite, а теперь в кодогенерации можно свою vtable написать и инициализировать все поля быстро через mem*. До этого это было undefined behavior из-за нефиксированности имплементации vtable.
https://en.cppreference.com/w/cpp/memory/new/operator_delete. Cм 27-30.
gcc.godbolt.org
Compiler Explorer - C++ (x86-64 clang (trunk))
struct Foo {
~Foo() { std::cout << "In Foo::~Foo()\n"; }
void operator delete(Foo *p, std::destroying_delete_t) {
std::cout
<< "In Foo::operator delete(Foo *, std::destroying_delete_t)\n";
p->~Foo();
::operator…
~Foo() { std::cout << "In Foo::~Foo()\n"; }
void operator delete(Foo *p, std::destroying_delete_t) {
std::cout
<< "In Foo::operator delete(Foo *, std::destroying_delete_t)\n";
p->~Foo();
::operator…
Недавно понадобилось снова открыть документацию и посмотреть что значат регистры %fs и %gs: я помню что-то про thread local storage, но не более.
Наткнулся на очень красивый ответ об истории
https://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for/10810340
Наткнулся на очень красивый ответ об истории
https://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for/10810340
The original intention behind the segment registers was to allow a program to access many different (large) segments of memory that were intended to be independent and part of a persistent virtual store. The idea was taken from the 1966 Multics operating system, that treated files as simply addressable memory segments. No BS "Open file, write record, close file", just "Store this value into that virtual data segment" with dirty page flushing.
...
There was still a need for threads to access thread local store, and each thread needed a a pointer ... somewhere in the immediately accessible thread state (e.g, in the registers) ... to thread local store. Since Windows and Linux both used FS and GS (thanks Nick for the clarification) for this purpose in the 32 bit version, AMD decided to let the 64 bit segment registers (GS and FS) be used essentially only for this purpose (I think you can make them point anywhere in your process space; I don't know if the application code can load them or not). Intel in their panic to not lose market share to AMD on 64 bits, and Andy being retired, decided to just copy AMD's scheme.
Stack Overflow
What is the "FS"/"GS" register intended for?
So I know what the following registers and their uses are supposed to be:
CS = Code Segment (used for IP)
DS = Data Segment (used for MOV)
ES = Destination Segment (used for MOVS, etc.)
SS = Stack
CS = Code Segment (used for IP)
DS = Data Segment (used for MOV)
ES = Destination Segment (used for MOVS, etc.)
SS = Stack
Long time no see
1. Мне очень понравилась статья о memory profiling от Дениса: https://easyperf.net/blog/2024/02/12/Memory-Profiling-Part1
В целом он очень правильно описал основы -- вам нужно смотреть на Virtual Memory как общее пространство и лимит ядра, RSS как внутреннюю работу и memory footprint -- сколько памяти в каждые X ms вы трогаете. Например, если вы видете много спайков в количестве тронутой памяти, то скорее всего у вас много локальных и мелкоживучих аллокаций, можно задать вопрос, можно ли что-то переиспользовать и улучшить кеш локальность.
Если говорить на практике, то я очень сильно смещён в сторону TCMalloc и jemalloc, друг у друга списываем, по паре людей в Google и Meta занимаются практически исключительно ими, так что должно быть достаточно подтюнено. Хорошое представление о том, как написать аллокатор для ваших десктопов, можно почитать тут. Рассказывается о ptmalloc2 используемый в линуксовых дистрибутивах, о том как в целом разделять аллокации и тд. Про память я в этом блоге писал много.
2. LZ4 получил уровень компрессии что-то между Fast и Hash Chain. Напомним, что LZ компрессия работает как команды "вставить текст как есть" и "скопировать из уже раскодированного текста и записать оффсет".
Fast уровень работает так:
Возьмём хеш таблицу, будем добавлять в неё 4 байта с каждой позиции. Если нашли такие же 4 байта, смотрим сколько байт совпало и добавляем инструкцию о копировании из предыдущего. Минусы такого подхода, что он никак не учитывает то, как дешёво кодировать оффсеты, например, стоит ли взять совпадение подальше и больше покрыть байт или поближе и закодировать оффсет подешевле. Из этого появился алгоритм Hash Chain -- запомним очень много позиций и будем оценивать, что из них дешевле. Hash Chain много добавляет всяких проблем с промахами по кешам и поэтому достаточно медленный.
Добавили уровень, который что-то посередине, а именно использует 2 хеш таблицы -- для 8 байт и для 4-5 байт. Сначала смотрит, нет ли подлиннее данных, а потом уже покороче как в fast. Скорость просаживается меньше, поэтому и получается где-то посередине.
3. Тут прошёл FOSDEM 2024, много всякого интересного, больше всего понравилось посмотреть, а что же такое load average в Linux (и почему не стоит на него смотреть) и бесконечный лист оптимизаций Query Planner в PostgreSQL
1. Мне очень понравилась статья о memory profiling от Дениса: https://easyperf.net/blog/2024/02/12/Memory-Profiling-Part1
В целом он очень правильно описал основы -- вам нужно смотреть на Virtual Memory как общее пространство и лимит ядра, RSS как внутреннюю работу и memory footprint -- сколько памяти в каждые X ms вы трогаете. Например, если вы видете много спайков в количестве тронутой памяти, то скорее всего у вас много локальных и мелкоживучих аллокаций, можно задать вопрос, можно ли что-то переиспользовать и улучшить кеш локальность.
Если говорить на практике, то я очень сильно смещён в сторону TCMalloc и jemalloc, друг у друга списываем, по паре людей в Google и Meta занимаются практически исключительно ими, так что должно быть достаточно подтюнено. Хорошое представление о том, как написать аллокатор для ваших десктопов, можно почитать тут. Рассказывается о ptmalloc2 используемый в линуксовых дистрибутивах, о том как в целом разделять аллокации и тд. Про память я в этом блоге писал много.
2. LZ4 получил уровень компрессии что-то между Fast и Hash Chain. Напомним, что LZ компрессия работает как команды "вставить текст как есть" и "скопировать из уже раскодированного текста и записать оффсет".
Fast уровень работает так:
Возьмём хеш таблицу, будем добавлять в неё 4 байта с каждой позиции. Если нашли такие же 4 байта, смотрим сколько байт совпало и добавляем инструкцию о копировании из предыдущего. Минусы такого подхода, что он никак не учитывает то, как дешёво кодировать оффсеты, например, стоит ли взять совпадение подальше и больше покрыть байт или поближе и закодировать оффсет подешевле. Из этого появился алгоритм Hash Chain -- запомним очень много позиций и будем оценивать, что из них дешевле. Hash Chain много добавляет всяких проблем с промахами по кешам и поэтому достаточно медленный.
Добавили уровень, который что-то посередине, а именно использует 2 хеш таблицы -- для 8 байт и для 4-5 байт. Сначала смотрит, нет ли подлиннее данных, а потом уже покороче как в fast. Скорость просаживается меньше, поэтому и получается где-то посередине.
3. Тут прошёл FOSDEM 2024, много всякого интересного, больше всего понравилось посмотреть, а что же такое load average в Linux (и почему не стоит на него смотреть) и бесконечный лист оптимизаций Query Planner в PostgreSQL
easyperf.net
Memory Profiling Part 1. Introduction | Easyperf
Тут вышел доклад от человека, который стандартизировал атомарные операции в С++ про то, что он понял за 15+ лет от этого дела.
Сначала скажу, что вокруг этой темы образовалось столько элитарности, что иногда хочется закрыться, сказать, что ничего не понял, и уйти. Но кто-нибудь будет тебе рассказывать, что всё понял. Относитесь к этому со скептизмом. Если человек, который это сделал говорит, что не понимает некоторые части, не стоит верить тому, кто говорит, что понимает.
По сути: доклад достаточно самокритичный
* Показывает, что думали в 2008-2010, когда создавали. Например, тогда мультиядерные системы только появились и математические модели стали стандартом. Например, Intel до 2007 года не писала, что у них Total Store Order.
* К удивлению, большинство железок исправилось и теперь Sequentially Consistent операции стоят не сотни тактов, а единицы. И даже когда приводили в пример Arm, оказывается, что они тоже туда движутся. Разнообразие различных моделей становится всё менее важным.
* Никто не понимает memory_order_consume. Я тоже
* В какой-то степени было ошибкой класть математическую модель в стандарт. Разработчики не читают стандарт, разработчики компиляторов тоже. Получается писали только для рисерчеров. Тем не менее, какой-то формализм нужен. Открытый вопрос как это сделать для людей.
* Weak memory model теряет свои преимущества. Большинство атомарных операций это флаги, counters, кеши, намного реже lock free structures и подобное.
Ещё из забавного, примерно в это же время нашли deadlock баг в имплементации shared_mutex в кишках Windows API. С Windows Vista воспроизводится, почти уже 20 лет багу в таких примитивах.
Сначала скажу, что вокруг этой темы образовалось столько элитарности, что иногда хочется закрыться, сказать, что ничего не понял, и уйти. Но кто-нибудь будет тебе рассказывать, что всё понял. Относитесь к этому со скептизмом. Если человек, который это сделал говорит, что не понимает некоторые части, не стоит верить тому, кто говорит, что понимает.
По сути: доклад достаточно самокритичный
* Показывает, что думали в 2008-2010, когда создавали. Например, тогда мультиядерные системы только появились и математические модели стали стандартом. Например, Intel до 2007 года не писала, что у них Total Store Order.
* К удивлению, большинство железок исправилось и теперь Sequentially Consistent операции стоят не сотни тактов, а единицы. И даже когда приводили в пример Arm, оказывается, что они тоже туда движутся. Разнообразие различных моделей становится всё менее важным.
* Никто не понимает memory_order_consume. Я тоже
* В какой-то степени было ошибкой класть математическую модель в стандарт. Разработчики не читают стандарт, разработчики компиляторов тоже. Получается писали только для рисерчеров. Тем не менее, какой-то формализм нужен. Открытый вопрос как это сделать для людей.
* Weak memory model теряет свои преимущества. Большинство атомарных операций это флаги, counters, кеши, намного реже lock free structures и подобное.
Ещё из забавного, примерно в это же время нашли deadlock баг в имплементации shared_mutex в кишках Windows API. С Windows Vista воспроизводится, почти уже 20 лет багу в таких примитивах.
Reddit
From the cpp community on Reddit: What we learned from C++ atomics and memory model standardization - Hans-J. Boehm - The Future…
Explore this post and more from the cpp community