La parte del código en un núcleo ATmega que hace setup () y loop () es la siguiente:
#include <Arduino.h>
int main(void)
{
init();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
Bastante simple, pero existe la sobrecarga de serialEventRun (); ahí.
Comparemos dos bocetos simples:
void setup()
{
}
volatile uint8_t x;
void loop()
{
x = 1;
}
y
void setup()
{
}
volatile uint8_t x;
void loop()
{
while(true)
{
x = 1;
}
}
La x y volátil es solo para garantizar que no esté optimizada.
En el ASM producido, obtienes resultados diferentes:
Puede ver el while (verdadero) solo realiza un rjmp (salto relativo) unas pocas instrucciones, mientras que loop () realiza una resta, comparación y llamada. Estas son 4 instrucciones vs 1 instrucción.
Para generar ASM como se indicó anteriormente, debe usar una herramienta llamada avr-objdump. Esto se incluye con avr-gcc. La ubicación varía según el sistema operativo, por lo que es más fácil buscarla por su nombre.
avr-objdump puede operar en archivos .hex, pero a estos les faltan la fuente original y los comentarios. Si acaba de crear código, tendrá un archivo .elf que contiene estos datos. Una vez más, la ubicación de estos archivos varía según el sistema operativo: la forma más fácil de localizarlos es activar la compilación detallada en las preferencias y ver dónde se almacenan los archivos de salida.
Ejecute el comando de la siguiente manera:
avr-objdump -S output.elf> asm.txt
Y examine la salida en un editor de texto.
main.c
utilizado por Arduino IDE. Sin embargo, no significa que la biblioteca HardwareSerial esté incluida en su boceto; En realidad no se incluye si no lo usa (por eso existeif (serialEventRun)
enmain()
la función Si no se utiliza la biblioteca HardwareSerial a continuación.serialEventRun
será nula, por lo tanto, no hay ninguna llamada.La respuesta de Cybergibbons describe muy bien la generación del código de ensamblaje y las diferencias entre las dos técnicas. Se pretende que sea una respuesta complementaria que analice el problema en términos de diferencias prácticas , es decir, cuánta diferencia tendrá cada enfoque en términos de tiempo de ejecución .
Variaciones de código
Hice un análisis que involucra las siguientes variaciones:
void loop()
(que se integra en la compilación)void loop()
(usando__attribute__ ((noinline))
)while(1)
(que se optimiza)while(1)
(agregando__asm__ __volatile__("");
. Esta es unanop
instrucción que evita la optimización del bucle sin generar gastos generales adicionales de unavolatile
variable)void loop()
con optimizadowhile(1)
void loop()
con no optimizadowhile(1)
Los bocetos se pueden encontrar aquí .
Experimentar
Ejecuté cada uno de estos bocetos durante 30 segundos, acumulando así 300 puntos de datos cada uno . Hubo una
delay
llamada de 100 milisegundos en cada bucle (sin el cual suceden cosas malas ).Resultados
Luego calculé los tiempos de ejecución medios de cada ciclo, resté 100 milisegundos de cada uno y luego grafiqué los resultados.
http://raw2.github.com/AsheeshR/Arduino-Loop-Analysis/master/Figures/timeplot.png
Conclusión
while(1)
bucle no optimizadovoid loop
es más rápido que un compilador optimizadovoid loop
.avr-gcc
y usando sus propios indicadores de optimización en lugar de depender del IDE de Arduino para ayudarlo (si necesita optimizaciones de microsegundos).NOTA: Los valores de tiempo reales no son significativos aquí, la diferencia entre ellos sí lo es. Los ~ 90 microsegundos de tiempo de ejecución incluyen una llamada a
Serial.println
,micros
ydelay
.NOTA2: Esto se realizó utilizando el IDE de Arduino y los indicadores de compilación predeterminados que proporciona.
NOTA3: El análisis (gráfico y cálculos) se realizó utilizando R.
fuente