A parte do código em um núcleo ATmega que executa setup () e loop () é a seguinte:
#include <Arduino.h>
int main(void)
{
init();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
Muito simples, mas há a sobrecarga do serialEventRun (); lá.
Vamos comparar dois esboços simples:
void setup()
{
}
volatile uint8_t x;
void loop()
{
x = 1;
}
e
void setup()
{
}
volatile uint8_t x;
void loop()
{
while(true)
{
x = 1;
}
}
O x e volátil é apenas para garantir que não seja otimizado.
No ASM produzido, você obtém resultados diferentes:
Você pode ver o tempo (true) apenas executando um rjmp (salto relativo) de volta algumas instruções, enquanto o loop () realiza uma subtração, comparação e chamada. São 4 instruções vs 1 instrução.
Para gerar o ASM como acima, você precisa usar uma ferramenta chamada avr-objdump. Isso está incluído no avr-gcc. A localização varia de acordo com o sistema operacional, portanto, é mais fácil procurar por nome.
O avr-objdump pode operar em arquivos .hex, mas estes estão ausentes da fonte e dos comentários originais. Se você acabou de criar código, terá um arquivo .elf que contém esses dados. Novamente, a localização desses arquivos varia de acordo com o sistema operacional - a maneira mais fácil de localizá-los é ativar a compilação detalhada nas preferências e ver onde os arquivos de saída estão sendo armazenados.
Execute o comando da seguinte maneira:
avr-objdump -S output.elf> asm.txt
E examine a saída em um editor de texto.
main.c
usado pelo IDE do Arduino. No entanto, isso não significa que a biblioteca HardwareSerial esteja incluída no seu esboço; na verdade, ele não está incluído se você não usá-lo (é por isso que existeif (serialEventRun)
emmain()
função Se você não usar HardwareSerial biblioteca de então.serialEventRun
será nulo, portanto, nenhuma chamada.A resposta de Cybergibbons descreve muito bem a geração de código de montagem e as diferenças entre as duas técnicas. Pretende-se que seja uma resposta complementar que analise a questão em termos de diferenças práticas , ou seja, quanta diferença uma das abordagens fará em termos de tempo de execução .
Variações de código
Fiz uma análise envolvendo as seguintes variações:
void loop()
(incluído na compilação)void loop()
(usando__attribute__ ((noinline))
)while(1)
(que é otimizado)while(1)
(adicionando__asm__ __volatile__("");
. Esta é umanop
instrução que impede a otimização do loop sem resultar em sobrecargas adicionais de umavolatile
variável)void loop()
com otimizadowhile(1)
void loop()
com não otimizadowhile(1)
Os esboços podem ser encontrados aqui .
Experimentar
Executei cada um desses esboços por 30 segundos, acumulando 300 pontos de dados cada . Havia uma
delay
chamada de 100 milissegundos em cada loop (sem que coisas ruins acontecessem ).Resultados
Calculei os tempos médios de execução de cada loop, subtraí 100 milissegundos de cada um e plotei os resultados.
http://raw2.github.com/AsheeshR/Arduino-Loop-Analysis/master/Figures/timeplot.png
Conclusão
while(1)
loop não otimizadovoid loop
é mais rápido do que um compilador otimizadovoid loop
.avr-gcc
e usando seus próprios sinalizadores de otimização em vez de depender do IDE do Arduino para ajudá-lo (se você precisar de otimizações de microssegundos).NOTA: Os valores de tempo reais não são significativos aqui, a diferença entre eles é. Os ~ 90 microssegundos de tempo de execução incluem uma chamada para
Serial.println
,micros
edelay
.NOTA2: Isso foi feito usando o Arduino IDE e os sinalizadores padrão do compilador que ele fornece.
NOTA3: A análise (plot e cálculos) foi realizada usando R.
fonte