2012-04-24 12 views
7

Busco durante un cierto código para revisar y he encontrado una espera ocupado como tales:¿Se optimizará un vacío para el bucle utilizado como sueño?

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

Me parece recordar que la lectura de estos bucles vacías se pueden optimizar de distancia. ¿Es esto lo que sucedería aquí o puede funcionar?

+1

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

+1

sí ............ –

+0

'us'? si este código funciona debe ser una CPU realmente lenta. –

Respuesta

12

Usted está a merced del compilador. De hecho, si es inteligente, detectará que es un noop. Por cierto, Neil Butterworth tiene un nice post donde también toca este tema.

+3

Si habilita las optimizaciones. – Kevin

+0

Gracias por la respuesta. Algo en el fondo de mi mente decía que este tipo de cosas podría ser un problema – Firedragon

+0

No realmente, incluso cuando no se puede emitir el código ensamblador en línea, el compilador no eliminará (y no puede) las expresiones con el lado local efectos. –

2

Algunos compiladores, como gcc, detectarán que se trata de un bucle vacío y específicamente pesimizan para eso, con la expectativa de que lo coloquen allí como un bucle de retardo. Puedes leer más al respecto en http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Non_002dbugs.html

Ten en cuenta que esto es específico del compilador, así que no cuentes con todos los compiladores.

+1

De hecho, me encontré con esto con mingw/msvc, donde tuve que depurar el código haciendo una espera ocupada en una variable modificada por otro hilo, y el ciclo fue 'optimizado' (en el caso mingw solo con -O2) en una sentencia jmp saltando sobre sí misma (es decir, un bucle infinito), fue muy divertido depurar eso. :) – aphax

4

Es algo terriblemente no portátil.

En algunos compiladores uno de estos puede obras (pero usted tiene que comprobar con plena optimización habilitada, la instrucción vacía puede ser desechado):

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

o:

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

Si Tenga la suerte de que su compilador proporcione una macro o una función intrínseca compilada en la instrucción de ensamblaje nop, algo así como __noop en MSVC.

Como último recurso simplemente puede añadir una sola instrucción de montaje (que es dependiente del compilador, puede ser __asm ​​o algo así) para ejecutar ... nada, así:

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

o (comprobar su documentación del compilador):

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

EDITAR
Si usted no tiene una instrucción noop y no se puede añadir el código de montaje (lo siento, ¿qué tipo de com piler que está usando?) puede confiar en la suposición de que una instrucción con un efecto secundario no se optimizará (o, como lo publicó @ouah, un acceso a una variable declarada volatile).

13

La respuesta es sí, el compilador puede optimizar el ciclo.

Utilice el calificador volatile para evitar la optimización:

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

Si está programando en el mundo incrustado lea la documentación de su compilador, ya que suelen proporcionar funciones de retardo que esperan a un cierto número de ciclos o microsegundos pasado en el parámetro.

Por ejemplo, avr-gcc tiene la siguiente función en util/delay.h:

void _delay_us(double __us); 
+0

Comentario muy útil. Gracias – Firedragon

+0

Lea un hermoso tutorial sobre por qué debería usarse la palabra clave volátil: http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword – Prabhpreet

2

Nada en el lenguaje estándar prohíbe, por lo que los compiladores pueden hacerlo si son capaces.

Vamos a descompilar GCC 4.8 para ver lo que hace el código

de entrada:

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

Compilar y descompilar:

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

Salida:

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 

El ciclo está allí: jle salta hacia atrás.

Con -O3:

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

que acaba devuelve 0. Por lo tanto, fue optimizado por completo.

El mismo análisis se puede hacer para cualquier compilador.

Ver también

Cuestiones relacionadas