Влияние несущественных изменений кода на производительность при использовании GCC
Nadav Amit, разработчик ядра Linux из компании VMware, поделился результатом исследования особенностей оптимизации в GCC небольших функций ядра. Исследование было проведено после того, как разработчик столкнулся с непонятным феноменом - внесение несущественных изменений в код ядра, приводили к небольшому, но заметному снижению производительности в тестах. Примечательно, что подобные вносимые изменения были оптимизациями и теоретически должны были увеличить производительность, но на деле производительность падала.
Дело оказалось в том, что GCC принимает решение об использовании inline-развертывания функций в зависимости от результатов оценки размера результирующего кода (даже если функция определена с ключевым словом "inline"). Компилятор не учитывает фактический размер результирующег кода, а пытается прогнозировать его на основе числа переводов строк ("&92;n") и разделителей (";") в исходном тексте.
Подобная особенность может приводить к таким казусам как снижение производительности при добавлении не влияющих на результирующий код макросов или в новых версиях GCC даже при замене символов табуляции на пробелы.
Разница в производительности особенно бросается в глаза для функций с ассемблерными вставками, для которых добавляется излишняя обертка вместо прямой подстановки нескольких указанных в функции инструкций. В ядре Linux проблему представляют компактные функции с макросами WARN, для которых оказалось, что не выполняется ожидаемая inline-оптимизация.
Автор исследования делает вывод, что требуется создание новых средств для контроля за поведением расширенных возможностей компилятора и анализа эффективности результирующих исполняемых файлов. В настоящее же время единственным способом убедиться, что код сгенерирован именно так как рассчитывал разработчик остается ручное инспектирование итоговых машинных инструкций.