Jak zapobiec optymalizacji przez GCC pętli czekania?

Chcę napisać firmware kodu C dla mikrokontrolerów Atmel AVR. Skompiluję go używając GCC. Chcę również włączyć optymalizacje kompilatorów (-Os lub -O2), ponieważ nie widzę powodu, aby ich nie włączać, a prawdopodobnie wygenerują lepszy montaż szybciej niż ręczne pisanie assembly.

Ale chcę mały kawałek kodu Nie zoptymalizowany. Chcę opóźnić wykonanie funkcji o jakiś czas, a więc chciałem napisać pętlę do-nothing tylko po to, aby zmarnować trochę czasu. Nie ma potrzeby bądź precyzyjny, poczekaj trochę.

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

Ponieważ dostęp do pamięci w AVR jest dużo wolniejszy, chcę, aby i i j były przechowywane w rejestrach procesora.


Update: właśnie znalazłem util / delay.h i util / delay_basic.h z AVR Libc. Chociaż w większości przypadków lepszym pomysłem może być użycie tych funkcji, to pytanie pozostaje ważne i interesujące.


Podobne pytania:

Author: Denilson Sá Maia, 2011-08-16

8 answers

Rozwinąłem tę odpowiedź po linku z dmckee ' s answer , ale ma inne podejście niż jego / jej odpowiedź.

Atrybuty funkcji dokumentacja z GCC wspomina:

noinline Ten atrybut funkcji uniemożliwia rozważenie funkcji do inliningu. Jeśli funkcja nie ma efektów ubocznych, istnieją optymalizacje inne niż inlining, które powodują, że wywołania funkcji są optymalizowane, chociaż wywołanie funkcji jest na żywo. Aby takie połączenia nie były optymalizowane, umieść asm ("");

To dało mi ciekawy pomysł... Zamiast dodawać instrukcję nop w wewnętrznej pętli, próbowałem dodać pusty kod asemblera, tak:
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i)
        asm("");
}
I zadziałało! Pętla ta nie została zoptymalizowana i nie dodano żadnych dodatkowych instrukcji nop.

Co więcej, jeśli użyjesz volatile, gcc zapisze te zmienne w pamięci RAM i doda kilka ldd i std, aby skopiować je do tymczasowe rejestry. Z drugiej strony takie podejście nie wykorzystuje volatile i nie generuje takich kosztów.


Update: jeśli kompilujesz kod używając -ansi lub -std, musisz zastąpić asm słowem kluczowym __asm__, tak jak opisano w dokumentacji GCC.

Ponadto, można również użyć __asm__ __volatile__("") Jeśli Instrukcja assembly musi być wykonana tam, gdzie ją umieścimy, (tzn. nie może być wyprowadzona z pętli jako optymalizacja).

 71
Author: Denilson Sá Maia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:47

Zadeklaruj i i j zmienne jako volatile. Uniemożliwi to kompilatorowi optymalizację kodu z udziałem tych zmiennych.

unsigned volatile char i, j;
 20
Author: ks1322,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 19:30:00

Nie jestem pewien, dlaczego jeszcze nie wspomniano, że takie podejście jest całkowicie błędne i łatwo łamane przez aktualizacje kompilatorów itp. O wiele bardziej sensowne byłoby określenie wartości czasu, na którą chcesz czekać i spin polling bieżącego czasu, aż pożądana wartość zostanie przekroczona. Na x86 możesz użyć rdtsc do tego celu, ale bardziej przenośnym sposobem byłoby wywołanie clock_gettime (lub wariantu dla Twojego systemu operacyjnego nie-POSIX), aby uzyskać czas. Obecny Linux x86_64 będzie nawet unikał syscall dla clock_gettime i używać rdtsc wewnętrznie. Lub, jeśli możesz obsłużyć koszt syscall, po prostu użyj clock_nanosleep na początek...

 4
Author: R..,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-01 20:31:06

Nie wiem z góry, czy wersja avr kompilatora obsługuje Pełny Zestaw #pragma s (te interesujące w linku wszystkie pochodzą z gcc w wersji 4.4), ale od tego Zwykle się zaczyna.

 3
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 19:30:32

Dla mnie, w GCC 4.7.0, pusty asm został zoptymalizowany z-O3 (nie próbowałem z-O2). a używanie i++ w rejestrze lub lotnych skutkowało dużą karą za wydajność (w moim przypadku).

To, co zrobiłem, to połączenie z inną pustą funkcją, której kompilator nie mógł zobaczyć podczas kompilacji "programu głównego"

W Zasadzie to:

Utworzono " helper.c " z tą funkcją zadeklarowaną (funkcja pusta)

void donotoptimize(){}

Następnie skompilowany " GCC helper.Pomocnik c-C-O.o" i wtedy

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

To dało mi najlepsze wyniki (i z mojego przekonania, żadnych kosztów w ogóle, ale nie mogę przetestować, ponieważ mój program nie będzie działać bez niego :) )

Myślę, że powinno działać również z icc. Może nie, jeśli włączysz optymalizacje łączenia, ale z gcc tak.

 2
Author: BiS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-18 21:06:52

Umieścić tę pętlę w osobnym .plik c i nie Optymalizuj tego jednego pliku. Jeszcze lepiej napisz tę rutynę w asemblerze i wywołaj ją z C, tak czy inaczej optymalizator nie będzie się angażował.

Czasami robię coś zmiennego, ale normalnie tworzę funkcję asm, która po prostu zwraca put a call to that function the optimizer will make the for / while loop tight but it wont optimized it out bo it has to make all the calls to the dummy function. Odpowiedź NOP od Denilson Sá robi to samo, ale jeszcze mocniej...

 1
Author: old_timer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 22:16:06

Wprowadzenie lotnego asm powinno pomóc. Możesz przeczytać więcej na ten temat tutaj: -

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

Jeśli pracujesz na Windows, możesz nawet spróbować umieścić kod pod pragmas, jak wyjaśniono szczegółowo poniżej: -

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

Mam nadzieję, że to pomoże.

 1
Author: Groovy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-17 19:18:34

Możesz również użyć register keyword . Zmienne zadeklarowane za pomocą rejestru są przechowywane w rejestrach procesora.

W Twoim przypadku:

register unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}
 -1
Author: Michel Megens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-06 13:14:19