Keil: уровни оптимизации компилятора и отладка |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||
Компилятором выполняется прецизионная оптимизация, которая зависит от выбранного уровня оптимизации (от оптимизации производительности или размера кода). Компилятор поддерживает следующие уровни оптимизации. 0: минимальная оптимизация (опция -O0). Большинство оптимизаций выключено. Когда разрешена отладка, этот вариант опции оптимизации дает самый лучшую отладку по исходному коду и просмотру переменных (debug view), потому что структура генерируемого кода точно соответствует исходному коду. Вся оптимизация, которая мешает отладочному представлению, отключена. В частности: • Точки останова (breakpoints) могут быть установлены только в любое достижимое место, включая "мертвый" код. Замечания: 1. Хотя вид режима отладки, предоставляемый опцией -O0, наиболее близко соответствует исходному коду, пользователи могут предпочесть режим отладки опции -O1, потому что этот вариант оптимизации улучшает качество кода без изменения его фундаментальной структуры. 1: ограниченная оптимизация (-O1). Компилятор выполняет только оптимизацию, которая может быть описана отладочной информацией. При этом из кода удаляются не используемые inline-функции и не используемые static-функции. Отключение оптимизаций серьезно ухудшает качество режима отладки (debug view). Если опция -O1 используется вместе с опцией -debug, то этот вариант в основном дает удовлетворительную возможность отладки с хорошей плотностью кода. Отличия режима отладки опций -O1 и –O0: • В "мертвом" коде нельзя поставить точки останова. Уровень оптимизации –O1 дает хорошее соответствие между исходным кодом и объектным кодом, особенно если в исходном коде нет "мертвого" кода. Объем генерируемого кода может быть значительно меньше, чем код опции –O0, которая может упростить анализ объектного кода. 2: высокий уровень оптимизации (-O2). Если используется опция --debug, то вид режима отладки может быть менее, чем удовлетворительный, потому что не всегда соблюдается соответствие между объектным кодом и исходным кодом. Компилятор может выполнить оптимизации, которые не могут быть описаны отладочной информацией. Опция -O2 это уровень оптимизации по умолчанию. Отличия режима отладки от опции –O1: • Соответствие исходного кода объектному коду может находиться по принципу "многое к одному", потому есть возможность повторений исходного кода в разных местах, которые будут преобразованы к одному месту в файле, и применяется более агрессивное планирование выполнения инструкций. 3: максимальная оптимизация (-O3). Когда отладка разрешена, эта опция обычно дает плохую отладку по исходному коду. ARM рекомендует применять более слабые уровни оптимизации. Если Вы совместно используете опции -O3 и -Otime, то компилятор будет выполнять дополнительные оптимизации, которые будут более агрессивны, такие как: • Высокоуровневые скалярные оптимизации, включая разворачивание циклов. Это может дать значительный выигрыш в производительности ценой небольшого увеличения размера кода, но присутствует риск увеличения времени сборки. Эти оптимизации эффективно меняют исходный входной код, в результате чего объектный код получает наименьшее соответствие исходному коду, и получается самый плохой debug view. Опция --loop_optimization_level=option управляет величиной оптимизации цикла, которая применяется опциями –O3 –Otime. Чем больше величина оптимизации цикла, тем хуже соответствие между исходным и объектным кодом. Для дополнительной информации по высокоуровневым трансформациям исходного кода при опциях –O3 –Otime используйте опцию командной строки --remarks. Из-за того, что оптимизация влияет на соответствие объектного кода исходному коду, выбор уровня оптимизации -Ospace и -Otime обычно влияет на поведение режима отладки (debug view). Опция -O0 самый лучший вариант из всех, если требуется упрощенный debug view. Выбор -O0 обычно увеличивает размер образа ELF по объему от 7 до 15%. Чтобы уменьшить размер отладочных таблиц, используйте опцию --remove_unneeded_entities. [Результаты тестирования опций оптимизации Keil] Мне было интересно узнать, как влияют опции оптимизации -O0, -O1, -O2 и -O3 на скорость выполнения кода и на затраты памяти под код и переменные. Проверял на Keil uVision 5.34.0.0, сравнивал время выполнения задержки на простом цикле и размер затраченных ресурсов в проекте. Результаты свел в таблицу:
Очевидно, что в большинстве случаев имеет смысл использовать опцию -O1, хотя полезно проверять поведение кода для разных уровней оптимизации, с целью поиска потенциальных ошибок. Хорошо спроектированный код должен показывать разные результаты по затратам ресурсов, но работать одинаково для всех уровней оптимизации. [-Otime] Выполняет оптимизацию для уменьшения времени выполнения ценой возможного увеличения объема кода. Используйте эту опцию, если время выполнения более важно, чем размер кода. Если с опцией -Otime компилируется следующий код: while (expression)
body;
В результате получится следующий преобразованный код: if (expression) { do body; while (expression); } [-Ospace] Выполняет оптимизацию для уменьшения объема кода ценой возможного увеличения времени его выполнения. Используйте эту опцию, если объем генерируемого кода более важен, чем производительность. Например, когда выбрана опция -Ospace, большие по объему, но совпадающие по содержимому куски кода могут быть преобразованы в вызовы внешних функций. Если необходимо, то можно компилировать критичные по времени выполнения части кода с опцией -Otime, а остальные части с опцией -Ospace. Если не указана опция -Otime, то компилятор по умолчанию предполагает -Ospace. [#pragma On, #pragma Otime, #pragma Ospace] Существует возможность с помощью директивы #pragma изменить поведение оптимизатора компилятора, которое было задано в командной строке. Например, если в командной строке была опция -O0 (не применять оптимизацию), и посередине модуля применить директиву #praqma -O3, то все функции за этой, до конца файла, директивой будут скомпилированы с максимальным уровнем оптимизации. Ниже показан пример компиляции модуля кода с функциями function1, function2 и function3, когда компилятор armcc был вызван с опцией -O0. Здесь function1 будет скомпилирована без оптимизации, а функции function2 и function3 с максимальным уровнем оптимизации. void function1(void){ ... // Без оптимизации (armcc -O0) } #pragma O3
void function2(void){ ... // Оптимизирована на уровне O3 } void function3(void){ ... // Оптимизирована на уровне O3 } [#pragma push, #pragma pop] Если необходимо изменить поведение оптимизатора компилятора только для одной функции в модуле, то необходимо использовать директиву #pragma On (либо #pragma Otime, либо #pragma Ospace) в обрамлении директив #pragma push и #pragma pop. Например, когда модуль компилировался с командной строкой armcc -Ospace (по умолчанию): void function1(void){ ... // Оптимизирована по размеру кода (armcc -Ospace) } #pragma push
#pragma Otime
void function2(void){ ... // Оптимизирована по времени выполнения } #pragma pop
void function3(void){ ... // Оптимизирована по размеру кода (armcc -Ospace) } 4.12 Benefits of reducing debug information in objects and libraries 4.13 Methods of reducing debug information in objects and libraries 7.34 --debug, --no_debug 7.35 --debug_macros, --no_debug_macros 7.52 --dwarf2 7.53 --dwarf3 7.111 --multifile, --no_multifile 7.140 --remove_unneeded_entities, --no_remove_unneeded_entities [Ссылки] 1. Compiler optimization levels and the debug view site:keil.com. |