В последние несколько лет не слишком много происходит интересных компиляторных оптимизаций, которые помогают всем, тем не менее от версии к версии мы видим в районе 0.5-1% исправлений, учитывая, что апдейты происходят 2 раза в год, мы попадаем в Proebsting's law, что компиляторы становятся в 2 раза быстрее каждые 18 лет (1.005**36 ~ 1.20, 1.01**36 ~ 1.43), что было несколько раз опровергнуто, но остаётся забавной байкой :)
Я уже писал, что даже на архитектурах Arm и x86 в компиляторах полно ещё всякого делать, и the work is never done, но всегда выбираются пути наименьшего сопротивления и наиболее перспективные направления для вытаскивания процентов из разных бинарей -- Profile Guided Optimizations. Как будет исполняться код, зависит от данных, и тема как вытащить лучше оптимизации из профиля, одна из самых приносящих пользу в последнее время. Я думаю, если посчитать из открытых источников, то Inliner PGO+ThinLTO+PD Inlined String Proto, то будет в районе 7-10%, где ThinLTO даёт больше всех.
Rule of thumb сейчас, что большинство Profile Driven (PD) оптимизаций дают в раойне 0.5-1%, на сильно больше расчитывать тяжело и если вы сможете что-то такое придумать, я думаю вы спокойно можете стать легендой компиляторостроения.
На этот раз о двух вещах.
Profile driven cmov
https://discourse.llvm.org/t/rfc-cmov-vs-branch-optimization/6040
Эта оптимизация забавна тем, что иногда код с if условиями сложно предугадать, а profile driven оптимизации исторически наоборот, любят ставить горячий код поближе.
Плюсы cmov в том, что нет branch, минусы, что все данные подготовить надо перед инструкцией.
Идея в том, чтобы сделать cost модель, насколько дороже будет инструкция conditional mov, чем branch и применить её.
Agner Fog пишет про эвристику, которая не супер идеальна, но просто хороший дефолт:
Profile driven protobuf inlined string
В протобуфах есть поля вида string. Исторически они замаскированы под указателями, потому что люди чаще поля не используют, чем используют, а также чуть удобнее аллоцировать на общих кусках памяти под названием арены. Проблемы указателей, что их надо разыменовывать, и за это мы платим много. Если включить обычный std::string на всех, будет плохо, протобуфы разрастутся, аллокации на аренах будут сложнее и тд.
Прелесть в том, что некоторые строки очень маленькие и очень горячие, поэтому Small String Optimization может работать лучше, чем разыменовывание указателя. Решение простое, давайте мы посэмплим в проде размеры и rate с которым мы обращаемся к тем или иным полям (просто stacktraces) и перекомпилируем горячие и маленькие строки в InlinedStringField.
Снова в районе 0.5-1% выигрыша распределенного примерно по всему бинарю. Мы зарелизили InlinedStringField, но полный e2e профиль и оптимизации пока непонятно как. В целом, я думаю, что если очень надо, логику написать можно.
Красиво, две истории, когда вещи stack up среди сотен мест и дают хороший буст.
Я уже писал, что даже на архитектурах Arm и x86 в компиляторах полно ещё всякого делать, и the work is never done, но всегда выбираются пути наименьшего сопротивления и наиболее перспективные направления для вытаскивания процентов из разных бинарей -- Profile Guided Optimizations. Как будет исполняться код, зависит от данных, и тема как вытащить лучше оптимизации из профиля, одна из самых приносящих пользу в последнее время. Я думаю, если посчитать из открытых источников, то Inliner PGO+ThinLTO+PD Inlined String Proto, то будет в районе 7-10%, где ThinLTO даёт больше всех.
Rule of thumb сейчас, что большинство Profile Driven (PD) оптимизаций дают в раойне 0.5-1%, на сильно больше расчитывать тяжело и если вы сможете что-то такое придумать, я думаю вы спокойно можете стать легендой компиляторостроения.
На этот раз о двух вещах.
Profile driven cmov
https://discourse.llvm.org/t/rfc-cmov-vs-branch-optimization/6040
Эта оптимизация забавна тем, что иногда код с if условиями сложно предугадать, а profile driven оптимизации исторически наоборот, любят ставить горячий код поближе.
Плюсы cmov в том, что нет branch, минусы, что все данные подготовить надо перед инструкцией.
Идея в том, чтобы сделать cost модель, насколько дороже будет инструкция conditional mov, чем branch и применить её.
Agner Fog пишет про эвристику, которая не супер идеальна, но просто хороший дефолт:
Branch is faster than a conditional move if:
1. move is part of a dependence chain, especially a long loop-carried one,
2. and prediction rate is better than 75%; or
can avoid a lengthy calculation of non-chosen operand
Первое условие говорит, что ждать подготовки данных для cmov иногда бывает долго, а второе условие говорит, что если мы не выбираем какой-то операнд, то он не должен быть слишком тяжелым и предсказание branch не должно быть слишком хорошим (рандомные данные самое оно).
В итоге даже прототип дал 0.5-1% поиска гугла. Я думаю уже зарелизили. Мораль этой истории показала, что не было одного или даже десяти мест, которые всё исправили, а исправились в районе пары-тройки сотен мест, которые набрались и суммарно дали такой прирост. Что не может не радовать, потому что я долго верил, что компиляторный перф сильно зависит от топ $МАЛОЕ_КОЛВО функций. Эта история поучительна и для меня.
Практика:
В clang я обычно при написании кода если хочу cmov, то пишу тернарный оператор, а не if statement
uint64_t val = cond ? x : y; // usually cmov
if (cond) { val = x; } else { val = y; } // usually branch
Profile driven protobuf inlined string
В протобуфах есть поля вида string. Исторически они замаскированы под указателями, потому что люди чаще поля не используют, чем используют, а также чуть удобнее аллоцировать на общих кусках памяти под названием арены. Проблемы указателей, что их надо разыменовывать, и за это мы платим много. Если включить обычный std::string на всех, будет плохо, протобуфы разрастутся, аллокации на аренах будут сложнее и тд.
Прелесть в том, что некоторые строки очень маленькие и очень горячие, поэтому Small String Optimization может работать лучше, чем разыменовывание указателя. Решение простое, давайте мы посэмплим в проде размеры и rate с которым мы обращаемся к тем или иным полям (просто stacktraces) и перекомпилируем горячие и маленькие строки в InlinedStringField.
Снова в районе 0.5-1% выигрыша распределенного примерно по всему бинарю. Мы зарелизили InlinedStringField, но полный e2e профиль и оптимизации пока непонятно как. В целом, я думаю, что если очень надо, логику написать можно.
Красиво, две истории, когда вещи stack up среди сотен мест и дают хороший буст.