Как inline функции влияют на производительность и размер бинарника в C++
Что проверяют
Знаете ли вы, что ключевое слово inline в современном C++ почти не управляет встраиванием. Интервьюер ждёт, что вы разведёте правило одного определения и решение оптимизатора. Следом спрашивают, как проверить, встроилась ли функция, и может ли встраивание уменьшить бинарник.
Как отвечать
Начните с того, что у слова два разных смысла:
inlineкак ключевое слово: разрешает определять функцию в заголовке, который включают многие единицы трансляции, без нарушения ODR. Все определения обязаны совпадать, линкер оставит одно. Функции, определённые в теле класса, иconstexprфункции неявноinline. С C++17 бывают иinlineпеременные;- встраивание как оптимизация: компилятор решает сам по эвристикам, на
-O2встраивает и функции безinline, а на-O0почти ничего. Заставить можно через[[gnu::always_inline]]или__forceinlineв MSVC, запретить черезnoinline.
Дальше про эффект. Главная польза не в сэкономленных инструкциях вызова, а в том, что после встраивания тело видно в контексте: константы подставляются, лишние ветки исчезают, цикл векторизуется. Цена: код раздувается, растёт давление на кэш инструкций, и горячий цикл может перестать в него помещаться. Маленькая функция при встраивании бинарник часто даже уменьшает, потому что последовательность вызова длиннее её тела.
Встраивание между единицами трансляции даёт LTO (-flto), а PGO подсказывает компилятору, какие вызовы горячие. Проверяют по факту: -fopt-info-inline в GCC, -Rpass=inline в Clang, дизассемблер или Compiler Explorer, размер секций смотрят через bloaty или size.
Где спотыкаются
Говорят, что inline заставляет компилятор встроить функцию. Это лишь подсказка, и современные компиляторы в решении о встраивании на неё почти не опираются.
Путают inline и static в заголовке. У static функции каждая единица трансляции получает свою копию и свой адрес, у inline копия одна.
Измеряют эффект на отладочной сборке или не измеряют вовсе. Рекурсивные функции, виртуальные без девиртуализации и вызовы по указателю обычно не встраиваются, и выводы без профайлера здесь не работают.