для цикла игнорируется (оптимизируется?)

Я использую циклы for / while для реализации задержки в моем коде. Длительность задержки здесь не важна, хотя она достаточно велика, чтобы быть заметной. Вот фрагмент кода.

uint32_t i;

// Do something useful

for (i = 0; i < 50000000U; ++i)
{}

// Do something useful

Проблема, которую я наблюдаю, заключается в том, что этот цикл for не выполняется. Вероятно, компилятор игнорирует / оптимизирует его. Однако, если я квалифицирую счетчик цикла i как volatile, цикл for, кажется, выполняется, и я замечаю желаемую задержку в выполнении.

Такое поведение кажется немного противоречащим моему пониманию оптимизации компилятора с / без ключевого слова volatile.

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

Платформа, для которой я создаю, - это процессор Xtensa (от Tensilica), а компилятор C - это тот, который предоставляется Tensilica, компилятор Xtensa C / C ++, работающий с высочайшим уровнем оптимизации.

Я пробовал то же самое с gcc 4.4.7 с -o3 и высокими уровнями оптимизации. Кажется, что в этом случае задержка работает.


person LoneWolf    schedule 13.05.2015    source источник
comment
Компилятор c может обнаружить, что цикл for вообще ничего не делает, и может просто удалить весь цикл, когда i не является изменчивым.   -  person user1937198    schedule 13.05.2015
comment
Честно говоря, если бы я был компилятором, я бы установил i равным 50000000U, и все готово, особенно на высоких уровнях оптимизации. С volatile его можно изменить извне, поэтому я не мог оптимизировать его и читать из регистра / кеша / где бы то ни было на любой итерации.   -  person martin    schedule 13.05.2015
comment
Компилятор не заботится о сохранении ваших переменных или ваших циклов; он заботится о сохранении таких вещей, как ввод и вывод. Полностью допустимо вырывание бездействующей петли.   -  person user2357112 supports Monica    schedule 13.05.2015
comment
Кроме того, поскольку никто еще не упомянул об этом: не делайте этого. Это не способ создания задержки. Воспользуйтесь одной из функций, предназначенных для работы.   -  person user2357112 supports Monica    schedule 13.05.2015
comment
Я использую циклы for / while для реализации задержки в моем коде. во-первых, очень плохая идея ...   -  person glglgl    schedule 13.05.2015
comment
@glglgl Я знаю, что это плохая идея. Как я уже упоминал в своем вопросе, здесь не имеет значения длительность / точность задержки. Просто оно должно быть больше определенного минимального значения. Более того, среда, в которой я работаю, представляет собой однопроцессорную, без многопроцессорную прошивку без операционной системы.   -  person LoneWolf    schedule 13.05.2015
comment
Доступен ли системный вызов nanosleep или что-то подобное? Действительно ли требуется временная задержка или есть переход между состояниями, который может вызвать выполнение кода и т. Д.?   -  person rickhg12hs    schedule 13.05.2015
comment
@ user2357112 Как вы правильно заметили, это расплывчатый подход. Но в моем случае меня больше волнует эта часть оптимизации, чем точность задержки. В моем сценарии это действительно несущественно.   -  person LoneWolf    schedule 13.05.2015
comment
@LoneWolf Даже в этом случае ваша библиотека контроллера может предоставить чистое решение, как _delay_us() на AVR. Такие функции часто пишутся на ассемблере и довольно точны, если вы их используете. Даже если вам не нужна такая точность, это самое чистое решение, поскольку, как видите, любое другое решение можно оптимизировать.   -  person glglgl    schedule 13.05.2015
comment
См. Возможные дубликаты Цикла с нулевым временем выполнения и связанных Замедляют ли временные переменные мою программу?   -  person Shafik Yaghmour    schedule 13.05.2015
comment
Возможный дубликат Как предотвратить оптимизацию GCC занятый цикл ожидания?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 15.10.2016


Ответы (1)


Все дело в наблюдаемом поведении. Единственное наблюдаемое поведение вашего цикла - это то, что i будет 50000000U после цикла. Компилятору разрешено оптимизировать его и заменить на i = 50000000U;. Это i назначение также будет оптимизировано, потому что значение i не имеет наблюдаемых последствий.

Ключевое слово volatile сообщает компилятору, что запись и чтение из i имеют наблюдаемое поведение, что предотвращает его оптимизацию.

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

Все эти правила оптимизации соответствуют тому, что написано в стандарте C (см. Комментарии для ссылок).

Кроме того, если вам нужна задержка, используйте специализированную функцию (например, OS API), они надежны и не потребляют ресурсы ЦП, в отличие от задержки вращения, подобной вашей.

person ElderBug    schedule 13.05.2015
comment
Я думаю, что это описано в N1570 §5.1.2.3, особенно в параграфе 4: В абстрактной машине все выражения оцениваются в соответствии с семантикой. Фактической реализации не требуется оценивать часть выражения, если она может сделать вывод, что ее значение не используется и что никаких побочных эффектов не возникает (в том числе вызванных вызовом функции или доступом к изменчивому объекту). - person user694733; 13.05.2015
comment
Спасибо за идеи ElderBug. Это то, что я действительно искал; некоторая оговорка в стандарте C против этого. Что касается использования API-интерфейсов ОС для задержки, я работаю над одним процессором, прошивкой на «голое железо», и точность задержки здесь незначительна. Мне просто нужно убедиться, что оно больше определенного порогового значения. - person LoneWolf; 13.05.2015
comment
@ user694733 Спасибо, что указали на это. Это, безусловно, уменьшило хлопоты. :) - person LoneWolf; 13.05.2015
comment
@LoneWolf Для прошивки на «голом железе» у вас часто есть доступ к аппаратному таймеру. Один хороший метод для задержки - сохранить значение таймера, а затем зацикливаться, пока значение не превысит некоторую точку (рассчитанную с частотой таймера). Для этого вам просто нужен работающий таймер (или запустите его для этого), и он обеспечивает надежную минимальную задержку. - person ElderBug; 13.05.2015
comment
@LoneWolf Is - еще худшая идея для написания циклов задержки в прошивке на «голом железе», чем в каком-либо размещенном настольном приложении, потому что в «голой железной» системе у вас есть прямой доступ к высокоточным аппаратным таймерам. Так что используйте встроенные таймеры вашего MCU, они есть не просто так. - person Lundin; 13.05.2015
comment
@Lundin Я полностью с тобой согласен. Это довольно расплывчатый подход к реализации задержек. Но, как я уже сказал, меня больше беспокоит поведение компилятора при столкновении с таким условием. Может быть, мне следовало сформулировать свой вопрос как относящийся к реальной проблеме. Я просто использовал этот (читай случайный) подход, чтобы проверить правильность инициализации регистра устройства. - person LoneWolf; 13.05.2015