Влияние несущественных изменений кода на производительность при использовании GCC

Nadav Amit, разработчик ядра Linux из компании VMware, поделился результатом исследования особенностей оптимизации в GCC небольших функций ядра. Исследование было проведено после того, как разработчик столкнулся с непонятным феноменом — внесение несущественных изменений в код ядра, приводило к небольшому, но заметному снижению производительности в тестах. Примечательно, что подобные вносимые изменения были оптимизациями и теоретически должны были увеличить производительность, но на деле производительность падала.

Дело оказалось в том, что GCC принимает решение об использовании inline-развёртывания функций в зависимости от результатов косвенной оценки размера результирующего кода (даже если функция определена с ключевым словом «inline»). Компилятор не учитывает фактический размер результирующего кода, а пытается прогнозировать его. Для ассемблерных вставок прогнозирование делается на основе числа переводов строк («n») и разделителей («;») в исходном тексте.

Логика подобного расчёта связана с допуском, что одна строка представляет собой одну ассемблерную инструкцию. Но при этом не учитываются ситуации, когда в ассемблерных вставках применяется большое число директив для вычислений, несколько строк которых в результате приводят к генерации лишь одной небольшой инструкции. Подобная особенность может приводить к таким казусам как снижение производительности при добавлении несущественных макросов. Некоторые разработчики отмечают влияние на inline-развёртывание в новых версиях GCC даже замены символов табуляции на пробелы.

Разница в производительности особенно бросается в глаза для функций с ассемблерными вставками, для которых добавляется излишняя обёртка вместо прямой подстановки нескольких указанных в функции инструкций. В ядре Linux проблему представляют компактные функции с макросами WARN(), для которых оказалось, что не выполняется ожидаемая inline-оптимизация.

Автор исследования делает вывод, что требуется создание новых средств для контроля за поведением расширенных возможностей компилятора и анализа эффективности результирующих исполняемых файлов. В настоящее время единственным способом убедиться, что код сгенерирован именно так как рассчитывал разработчик остаётся ручное инспектирование итоговых машинных инструкций.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.