Как предотвратить GCC от оптимизации из занятого цикла ожидания?


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

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

/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}

так как доступ к памяти в AVR намного медленнее, я хочу i и j для хранения в регистрах процессора.


обновление: я только что нашел util / задержка.h и util / delay_basic.h С AVR Libc. Хотя в большинстве случаев было бы лучше использовать эти функции, этот вопрос остается актуальным и интересным.


обзоры вопросы:

8 54

8 ответов:

я разработал этот ответ после перехода по ссылке из ответ dmckee, но это требует иного подхода, чем его/ее ответ.

Атрибуты Функция документация от GCC упоминает:

noinline Этот атрибут функции предотвращает рассмотрение функции для встраивания. Если функция не имеет побочных эффектов, существуют оптимизации, отличные от встраивания, которые вызывают оптимизацию вызовов функций прочь, хотя вызов функции находится в прямом эфире. Чтобы такие вызовы не оптимизировались, поместите asm ("");

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

unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i)
        asm("");
}

и это сработало! Этот цикл не был оптимизирован, и никаких дополнительных инструкция.

что еще, если вы используете volatile, gcc будет хранить те переменные в ОЗУ и добавить кучу ldd и std копировать их во временные регистры. Этот подход, с другой стороны, не использует volatile и не создает такой нагрузки.


обновление: если вы компилируете код с помощью -ansi или -std, вы должны заменить asm ключевое слово __asm__, а описано в документации GCC.

кроме того, вы также можете использовать __asm__ __volatile__("") если сборка оператор должен выполняться там, где мы его ставим, (т. е. не должен быть выведен из цикла в качестве оптимизации).

объявить i и j переменные volatile. Это не позволит компилятору оптимизировать код с использованием этих переменных.

unsigned volatile char i, j;

Я не уверен, почему еще не было упомянуто, что этот подход полностью ошибочен и легко нарушается обновлениями компилятора и т. д. Было бы гораздо более разумно определить значение времени, которое вы хотите дождаться, и вращать опрос текущего времени до тех пор, пока не будет превышено желаемое значение. На x86 вы можете использовать rdtsc для этой цели, но более портативный способ будет назвать clock_gettime (или вариант для вашей не-POSIX ОС), чтобы получить время. Текущий x86_64 Linux будет даже избегать вызов на clock_gettime и использовать rdtsc внутренне. Или, если вы можете справиться со стоимостью syscall, просто используйте clock_nanosleep для начала...

Я не знаю, с верхней части моей головы, если версия AVR и компилятора поддерживает полный комплект #pragmas (Интересные в ссылке Все даты из gcc версии 4.4), но именно там вы обычно начинаете.

для меня, на GCC 4.7.0, пустой asm был оптимизирован в любом случае с-O3 (не пробовал с-O2). и использование i++ в регистре или volatile привело к большому штрафу за производительность (в моем случае).

то, что я сделал, было связано с другой пустой функцией, которую компилятор не мог видеть при компиляции "основной программы"

в основном это:

создал "помощника.c " с объявленной этой функцией (пустой функцией)

void donotoptimize(){}

затем скомпилирован " GCC helper.c -c -о помощнике.о" а потом

while (...) { donotoptimize();}

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

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

поместите этот цикл в отдельный .C-файл и не оптимизируйте этот один файл. Еще лучше написать эту процедуру в ассемблере и вызвать ее из C, в любом случае оптимизатор не будет участвовать.

Я иногда делаю летучую вещь, но обычно создаю функцию asm, которая просто возвращает поместите вызов этой функции оптимизатор сделает цикл for/while плотным, но он не будет оптимизировать его, потому что он должен сделать все вызовы фиктивной функции. НОП ответа от Денилсон Са делает то же самое, но еще жестче...

положить летучие asm должно помочь. Вы можете прочитать больше об этом здесь:-

http://www.nongnu.org/avr-libc/user-manual/optimization.html

Если вы работаете на Windows, вы даже можете попробовать поместить код под прагмы, как подробно описано ниже: -

https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data

Надежда эта помощь.

вы также можете использовать зарегистрироваться ключевое слово. Переменные, объявленные с регистром, хранятся в регистрах ЦП.

в вашем случае:

register unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}