Действительно ли полезно отключать оптимизацию на этапах разработки и отладки?

15

Я прочитал Программирование 16-битных микроконтроллеров PIC в C , и в книге есть это утверждение:

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

Признаюсь, я был немного смущен. Я не понял, сказал ли автор это из-за периода оценки C30 или это действительно хорошая практика.

Я хотел бы знать, если вы действительно используете эту практику, и почему?

Даниэль Грилло
источник

Ответы:

16

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

Возможно, вы поймете, что вы вычисляете число, с которым потом ничего не делаете, прежде чем переписывать. В этом случае это может устранить бесполезные вычисления.

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

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

Майкл Кон
источник
5
Контрапункт этому аргументу состоит в том, что оптимизатор, вероятно, сделает вещи меньше и / или быстрее, и если у вас есть код с ограничением по времени или размеру, вы можете что-то сломать, отключив оптимизацию, и тратить свое время на отладку проблемы, которая не ' т действительно существует. Конечно, отладчик может сделать ваш код медленнее и больше.
Кевин Вермеер
Где я могу узнать больше об этом?
Даниэль Грилло
Я не работал с компилятором C30, но для компилятора C18 было примечание к приложению / руководство для компилятора, которое охватывало, какие оптимизации он поддерживал.
Mark
@O Engenheiro: проверьте документацию по компилятору, какие оптимизации он поддерживает. Оптимизация сильно варьируется в зависимости от компилятора, библиотек и целевой архитектуры.
Майкл Кохне
Опять же, не для компилятора C30, но gcc публикует LONG-список различных оптимизаций, которые можно применить. Вы также можете использовать этот список, чтобы получить детальную оптимизацию, если у вас есть особая структура управления, которую вы хотите оставить нетронутой. Список здесь: gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Кевин Вермеер,
7

Я отправил этот вопрос Джеку Гэнслу, и вот что он мне ответил:

Daniel,

Я предпочитаю отлаживать, используя любые оптимизации, которые будут в выпущенном коде. НАСА говорит: «Проверь, что ты летишь, лети, что ты проверяешь». Другими словами, не проводите тестирование, а затем меняйте код!

Однако иногда приходится отключать оптимизацию, чтобы отладчик работал. Я пытаюсь отключить их только в модулях, над которыми я работаю. По этой причине я верю в то, чтобы файлы были маленькими, скажем, несколько сотен строк кода или около того.

Всего наилучшего, Джек

Даниэль Грилло
источник
Я думаю, что есть негласное различие между двумя параграфами в этом ответе. Тестирование относится к процедуре, которая должна продемонстрировать, что мягкое, твердое и / или аппаратное оборудование работает должным образом. Отладка - это процесс, в котором код проходит пошаговую инструкцию, чтобы понять, почему он не работает [пока].
Кевин Вермеер
Приятно иметь выбор. Таким образом, тестирование может охватывать больше разновидностей / перестановок с оптимизацией и без нее.
@reemrevnivek, когда вы отлаживаете, вы тоже не тестируете?
Даниэль Грилло
@O Engenheiro - Нет. Я только отлаживаю, если тест не пройден.
Кевин Вермеер
6

Зависит, и это в целом верно для всех инструментов, не только C30.

Оптимизации часто удаляют и / или реструктурируют код различными способами. Ваш оператор switch может быть переопределен конструкцией if / else или в некоторых случаях может быть удален все вместе. y = x * 16 может быть заменено серией левых сдвигов и т. д., хотя этот последний тип оптимизации, как правило, все еще можно пройти, в основном это реструктуризация оператора управления, которая получает ya.

Это может сделать невозможным пошаговое выполнение отладчиком вашего кода C, поскольку структуры, которые вы определили в C, больше не существуют, они были заменены или переупорядочены компилятором в нечто, что, по мнению компилятора, будет быстрее или займет меньше места. Это также может сделать невозможным установку точек останова из списка C, так как инструкция, по которой вы взломали, больше не существует. Например, вы можете попытаться установить точку останова внутри оператора if, но компилятор, возможно, удалил ее, если. Вы можете попытаться установить точку останова в цикле while или for, но компилятор решил развернуть этот цикл, чтобы он больше не существовал.

По этой причине, если вы можете отлаживать с отключенной оптимизацией, это обычно проще. Вы всегда должны перепроверять с оптимизацией на. Это единственный способ узнать, что вы пропустили важное, volatileи оно вызывает периодические сбои (или некоторые другие странности).

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

Другая проблема может заключаться в том, чтобы встраивать код в ОК, вам может потребоваться оптимизация плотности кода, чтобы просто вставить ваш код в чип. Это одна из причин, почему обычно лучше начинать с наибольшей емкости ПЗУ в семействе и выбирать только меньшую для производства, после того как код заблокирован.

отметка
источник
5

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

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

semaj
источник
1
Но один шаг кода может быть практически невозможен при включенной оптимизации. Отлаживайте с отключенной оптимизацией и запускайте свои модульные тесты с кодом выпуска.
Ракетный магнит
3

Моя обычная стратегия состоит в том, чтобы развиваться с окончательной оптимизацией (макс. Для размера или скорости в зависимости от ситуации), но временно отключить оптимизацию, если мне нужно отладить или отследить что-либо. Это снижает риск появления ошибок в результате изменения уровней оптимизации.

Типичный режим отказа - когда увеличение оптимизации приводит к появлению ранее невидимых ошибок из-за того, что вы не объявили переменные как изменчивые там, где это необходимо - важно сообщить компилятору, какие вещи не следует «оптимизировать».

mikeselectricstuff
источник
2

Используйте любую форму, которую вы собираетесь выпустить, отладчики и компиляция для отладки скрывают множество ошибок, которые вы не видите, пока не скомпилируете для выпуска. К тому времени гораздо труднее найти эти ошибки, чем отлаживать по ходу работы. 20 с лишним лет, и у меня никогда не было использования gdb или другого подобного отладчика, нет необходимости наблюдать за переменными или одним шагом. От сотен до тысяч строк в день. Так что это возможно, не нужно думать иначе.

Компиляция для отладки, а затем компиляция для релиза может и займет от двух до более чем двухкратных усилий. Если вы попали в привязку и должны использовать такой инструмент, как отладчик, то скомпилируйте его для работы с конкретной проблемой, а затем вернитесь к нормальной работе.

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

Старожил
источник
1
+1 только для уточнения вашего хорошего ответа: часто компиляция в режиме «отладки» будет дополнять стек / кучу переменных с нераспределенным пространством, чтобы уменьшить небольшие ошибки записи и форматирования строк. Вы будете чаще получать хорошие сбои во время выполнения, если будете компилировать в релизе.
Мортен Дженсен
2

Я всегда разрабатываю код с -O0 (опция gcc отключает оптимизацию). Когда я почувствую, что я нахожусь в точке, где я хочу начать выпускать вещи, я начну с -Os (оптимизация по размеру), поскольку, как правило, чем больше кода вы можете сохранить в кэше, тем лучше, даже если он не супер оптимизирован.

Я считаю, что GDB намного лучше работает с кодом -O0, и намного легче следовать, если вам нужно войти в сборку. Переключение между -O0 и -Os также позволяет вам увидеть, что компилятор делает с вашим кодом. Время от времени это довольно интересное обучение, и оно также может выявить ошибки компилятора ... те неприятные вещи, которые заставляют вас напрячься, пытаясь выяснить, что не так с вашим кодом!

Если мне действительно понадобится, я начну добавлять в -fdata-секции и -fcode-секции с помощью --gc-секции, которые позволяют компоновщику удалять целые функции и сегменты данных, которые фактически не используются. Есть много мелочей, с которыми можно повозиться, чтобы попытаться уменьшить или ускорить процесс, но в целом это единственные приемы, которые я в конечном итоге использую, и все, что должно быть меньше или быстрее, я передам -assemble.

akohlsmith
источник
2

Да, отключение оптимизаций во время отладки уже давно является лучшей практикой по трем причинам:

  • (а) если вы собираетесь выполнить пошаговую программу с помощью высокоуровневого отладчика, это немного менее запутанно.
  • (a) (устарело), ​​если вы собираетесь отлаживать программу за один шаг с помощью отладчика на ассемблере, это намного менее запутанно. (Но зачем вам это, когда вы можете использовать отладчик высокого уровня?)
  • (б) (давно устарел) вы, вероятно, собираетесь запустить этот конкретный исполняемый файл только один раз, затем внести некоторые изменения и перекомпилировать. Это пустая трата времени на то, чтобы подождать дополнительные 10 минут, пока компилятор «оптимизирует» этот конкретный исполняемый файл, когда это сэкономит менее 10 минут времени выполнения. (Это больше не относится к современным ПК, которые могут скомпилировать типичный исполняемый файл микроконтроллера с полной оптимизацией менее чем за 2 секунды).

Многие люди идут еще дальше в этом направлении и грузят с включенными утверждениями .

davidcary
источник
Одноступенчатый ассемблерный код может быть очень полезен для диагностики случаев, когда исходный код на самом деле указывает что-то отличное от того, как он выглядит (например, "longvar1 & = ~ 0x40000000; longvar2 & = ~ 0x80000000;") или когда компилятор генерирует код с ошибками , Я выследил некоторые проблемы с использованием отладчиков машинного кода, которые, на самом деле, я не думаю, что мог бы выследить любым другим способом.
суперкат
2

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

stevenvh
источник
2

это, безусловно, имеет смысл в случае точек останова ... так как компилятор может удалить множество операторов, которые на самом деле не влияют на память.

рассмотреть что-то вроде:

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

может быть полностью оптимизирован (потому что iникогда не читается). с точки зрения вашей точки останова будет выглядеть, что он пропустил весь этот код, когда его в принципе просто не было ... Я предполагаю, что именно поэтому в функциях типа сна вы часто будете видеть что-то вроде:

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;
Grady Player
источник
1

Если вы используете отладчик, я бы отключил оптимизацию и включил отладку.

Лично я считаю, что отладчик PIC вызывает больше проблем, чем помогает мне исправить.
Я просто использую printf () для USART для отладки моих программ, написанных на C18.

mjh2007
источник
1

Большинство аргументов против включения оптимизации в вашей компиляции сводятся к:

  1. проблемы с отладкой (подключение JTAG, точки останова и т. д.)
  2. неправильная синхронизация программного обеспечения
  3. Sh * T перестает работать правильно

ИМХО первые два законны, третий не так уж и много. Это часто означает, что у вас плохой код или вы полагаетесь на небезопасную эксплуатацию языка / реализации, или, может быть, автор просто фанат доброго дяди Undefined Behavior.

В блоге Embedded in Academia есть кое-что, что можно сказать о неопределенном поведении, и этот пост о том, как его используют компиляторы: http://blog.regehr.org/archives/761

Мортен Дженсен
источник
Другая возможная причина заключается в том, что компилятор может быть раздражающе медленным, когда оптимизация включена.
суперкат