¿Cómo evitar que GCC optimice un bucle de espera ocupado?


Quiero escribir un firmware de código C para microcontroladores Atmel AVR. Lo compilaré usando GCC. Además, quiero habilitar las optimizaciones del compilador (-Os o -O2), ya que no veo ninguna razón para no habilitarlas, y probablemente generarán un mejor ensamblado mucho más rápido que escribir ensamblado manualmente.

Pero quiero un pequeño fragmento de código no optimizado. Quiero retrasar la ejecución de una función por algún tiempo, y por lo tanto quería escribir un bucle de no hacer nada solo para perder algún tiempo. No es necesario sé preciso, espera un poco.

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

Dado que el acceso a la memoria en AVR es mucho más lento, quiero que i y j se mantengan en los registros de la CPU.


Actualización: Acabo de encontrar util/delay.h and util/delay_basic.h de AVR Libc. Aunque la mayoría de las veces podría ser una mejor idea usar esas funciones, esta pregunta sigue siendo válida e interesante.


Preguntas relacionadas:

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

8 answers

Desarrollé esta respuesta después de seguir un enlace de la respuesta de dmckee, pero toma un enfoque diferente a su respuesta.

Atributos de función la documentación de GCC menciona:

noinline Este atributo function evita que una función sea considerada para la inserción. Si la función no tiene efectos secundarios, hay optimizaciones que no sean inlining que hacen que las llamadas a funciones se optimicen, aunque la llamada a la función es en vivo. Para evitar que tales llamadas se optimicen, ponga asm ("");

Esto me dio una idea interesante... En lugar de agregar una instrucción nop en el bucle interno, intenté agregar un código ensamblador vacío allí, como este:

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

¡Y funcionó! Ese bucle no ha sido optimizado-out, y no se insertaron instrucciones adicionales nop.

Además, si usas volatile, gcc almacenará esas variables en RAM y agregará un montón de ldd y std para copiarlas registros temporales. Este enfoque, por otro lado, no usa volatile y no genera tal sobrecarga.


Actualización: Si está compilando código usando -ansi o -std, debe reemplazar la palabra clave asm por __asm__, como se describe en la documentación del CCG.

Además, también puede utilizar __asm__ __volatile__("") si su la instrucción assembly debe ejecutarse donde la colocamos, (es decir, no debe moverse fuera de un bucle como una optimización).

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:47

Declare i y j variables como volatile. Esto evitará que el compilador optimice el código que involucra estas variables.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 19:30:00

No estoy seguro de por qué no se ha mencionado aún que este enfoque está completamente equivocado y se rompe fácilmente con las actualizaciones del compilador, etc. Tendría mucho más sentido determinar el valor de tiempo que desea esperar y girar el sondeo de la hora actual hasta que se exceda el valor deseado. En x86 se podría usar rdtsc para este propósito, pero la forma más portátil sería llamar a clock_gettime (o la variante para su sistema operativo no POSIX) para obtener el tiempo. Linux actual x86_64 incluso evitará el syscall para clock_gettime y use rdtsc internamente. O, si puede manejar el costo de una llamada de sistema, simplemente use clock_nanosleep para comenzar...

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-01 20:31:06

No se desde el principio si la versión avr del compilador soporta el conjunto completo de #pragma s (los interesantes en el enlace todos datan de la versión 4.4 de gcc), pero ahí es donde normalmente empezarías.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 19:30:32

Para mí, en GCC 4.7.0, el asm vacío se optimizó de todos modos con-O3 (no lo intenté con-O2). y usar un i++ en register o volatile resultó en una gran penalización de rendimiento (en mi caso).

Lo que hice fue enlazar con otra función vacía que el compilador no podía ver al compilar el "programa principal"

Básicamente esto:

Creado "helper.c " con esta función declarada (función vacía)

void donotoptimize(){}

Luego compiló "gcc helper.c -c-o ayudante.o" y luego

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

Esto me dio los mejores resultados (y desde mi creencia, no hay gastos generales en absoluto, pero no puedo probar porque mi programa no funcionará sin él:))

Creo que también debería funcionar con la cpi. Tal vez no si habilitas las optimizaciones de vinculación, pero con gcc lo hace.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-18 21:06:52

Pon ese bucle por separado .c archivo y no optimizar que un archivo. Aún mejor escribir esa rutina en ensamblador y llamarla desde C, de cualquier manera el optimizador no se involucrará.

A veces hago lo volátil, pero normalmente creo una función asm que simplemente devuelve poner una llamada a esa función el optimizador hará el bucle for/while apretado pero no lo optimizará porque tiene que hacer todas las llamadas a la función dummy. La respuesta nop de Denilson Sá hace el lo mismo, pero aún más apretado...

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 22:16:06

Poner asm volátil debería ayudar. Puedes leer más sobre esto aquí: -

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

Si está trabajando en Windows, incluso puede intentar poner el código bajo pragmas, como se explica en detalle a continuación: -

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

Espero que esto ayude.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-17 19:18:34

También puede utilizar el registrar palabra clave . Las variables declaradas con registro se almacenan en registros de CPU.

En su caso:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-06 13:14:19