2012-04-24 10 views
7

Sto guardando oltre un certo codice di rivedere e sono imbattuto in un'attesa occupato come ad esempio:Verrà ottimizzato un ciclo per vuoto usato come sonno?

int loop = us*32; 
int x; 
for(x = 0;x<loop;x++) 
{ 
    /*do nothing*/  
} 

Mi sembra di ricordare che la lettura di queste anse vuote possono essere ottimizzati via. È questo che succederebbe qui o può funzionare?

+1

http://stackoverflow.com/questions/3527829/is-this -a-bug-in-the-intel-c-compiler-icc/3527862 # 3527862 –

+1

sì ............ –

+0

'noi'? se funziona questo codice, deve essere una CPU molto lenta. –

risposta

12

Sei in balia del compilatore. Effettivamente se è intelligente scoprirà che è un noop. Per inciso, Neil Butterworth ha uno nice post in cui tocca anche questo argomento.

+3

Se si abilitano le ottimizzazioni. – Kevin

+0

Grazie per la risposta. Era qualcosa nella parte posteriore della mia mente che diceva che questo genere di cose poteva essere un problema – Firedragon

+0

Non proprio, anche quando non puoi emettere un codice di assembly inline il compilatore non (e non può) buttare via le espressioni con il lato locale- effetti. –

2

Alcuni compilatori, come gcc, rileveranno che è un ciclo for vuoto e in particolare pessimize per quello, con l'aspettativa che lo mettiate lì come un ciclo di ritardo. Puoi leggere ulteriori informazioni al riguardo a http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Non_002dbugs.html

Attenzione, questo è specifico del compilatore, quindi non contare su tutti i compilatori.

+1

Infatti, mi sono imbattuto in questo con mingw/msvc, dove ho dovuto eseguire il debug del codice effettuando un'attesa impegnativa su una variabile modificata da un altro thread e il ciclo era "ottimizzato" (nel caso mingw solo con -O2) in una frase jmp che salta a se stessa (cioè un loop infinito), è stato molto divertente eseguire il debug di questo. :) – aphax

4

È qualcosa di terribilmente non portatile.

In alcuni compilatori una di queste opere può (ma si controlla con piena ottimizzazione abilitata, l'istruzione vuota può essere gettato via):

for (i = 0; i < spinCount;) 
    ++i; // yes, HERE 

o:

for (i = 0; i < spinCount; ++i) 
    ((void)0);  

Se E 'abbastanza fortunato che il tuo compilatore possa fornire una macro o una funzione intrinseca che verrà compilata nell'istruzione di assemblaggio nop, qualcosa come __noop in MSVC.

Come ultima risorsa si può semplicemente aggiungere una singola istruzione di montaggio (è compilatore dipendente, può essere __asm ​​o qualcosa di simile) per eseguire ... niente, in questo modo:

for (i = 0; i < spinCount; ++i) 
    __asm nop 

o (controllare il vostro compilatore documentazione):

for (i = 0; i < spinCount; ++i) 
    asm("nop"); 

EDIT
Se non si dispone di un'istruzione noop e non è possibile aggiungere il codice assembly (mi dispiace, che tipo di com piler che stai usando?) puoi contare sul presupposto che un'istruzione con un effetto collaterale non verrà ottimizzata (o, come pubblicato da @ouah, un accesso a una variabile dichiarata volatile).

13

La risposta è sì, il compilatore può ottimizzare il ciclo.

Utilizzare il qualificatore volatile per evitare l'ottimizzazione:

int loop = us * 32; 
volatile int x; 
for (x = 0; x < loop; x++) 
{ 
    /*do nothing*/  
} 

Se si sta programmando nel mondo embedded leggere la documentazione del compilatore come fanno di solito forniscono funzioni di ritardo che aspettano per un certo numero di cicli o microsecondi passato in parametro.

Per esempio, avr-gcc ha la seguente funzione nel util/delay.h:

void _delay_us(double __us); 
+0

Commento molto utile. Grazie – Firedragon

+0

Leggi un bellissimo tutorial sul perché la parola chiave volatile dovrebbe essere utilizzata: http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword – Prabhpreet

2

Nulla nel linguaggio standard vieta, in modo da compilatori può fare se sono in grado.

Diamo decompilare GCC 4.8 per vedere cosa fa il codice

ingresso:

int main() { 
    int i; 
    for(i = 0; i < 16; i++) 
     ; 
} 

Compilare e decompilare:

gcc -c -g -std=c99 -O0 a.c 
objudmp -S a.o 

uscita:

a.o:  file format elf64-x86-64 


Disassembly of section .text: 

0000000000000000 <main>: 
int main() { 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    int i; 
    for(i = 0; i < 16; i++) 
    4: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp) 
    b: eb 04     jmp 11 <main+0x11> 
    d: 83 45 fc 01    addl $0x1,-0x4(%rbp) 
    11: 83 7d fc 0f    cmpl $0xf,-0x4(%rbp) 
    15: 7e f6     jle d <main+0xd> 
    17: b8 00 00 00 00   mov $0x0,%eax 
     ; 
} 
    1c: 5d      pop %rbp 
    1d: c3      retq 

Il ciclo è lì: quello jle salta indietro.

Con -O3:

0000000000000000 <main>: 
    0: 31 c0     xor %eax,%eax 
    2: c3      retq 

che restituisce solo 0. Così è stato completamente ottimizzato via.

La stessa analisi può essere eseguita per qualsiasi compilatore.

Vedi anche