Der Teil des Codes auf einem ATmega-Core, der setup () und loop () ausführt, lautet wie folgt:
#include <Arduino.h>
int main(void)
{
init();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
Ziemlich einfach, aber es gibt den Overhead von serialEventRun (); da drin.
Vergleichen wir zwei einfache Skizzen:
void setup()
{
}
volatile uint8_t x;
void loop()
{
x = 1;
}
und
void setup()
{
}
volatile uint8_t x;
void loop()
{
while(true)
{
x = 1;
}
}
Das x und volatile soll nur sicherstellen, dass es nicht optimiert wird.
In der produzierten ASM erhalten Sie unterschiedliche Ergebnisse:
Sie können sehen, dass while (true) nur einen relativen Rücksprung (rjmp) durchführt, während loop () eine Subtraktion, einen Vergleich und einen Aufruf durchführt. Dies ist 4 Anweisungen vs 1 Anweisung.
Um ASM wie oben zu generieren, müssen Sie ein Tool namens avr-objdump verwenden. Dies ist in avr-gcc enthalten. Der Speicherort variiert je nach Betriebssystem, daher ist es am einfachsten, nach Namen zu suchen.
avr-objdump kann mit .hex-Dateien arbeiten, denen jedoch die ursprüngliche Quelle und die Kommentare fehlen. Wenn Sie gerade Code erstellt haben, verfügen Sie über eine .elf-Datei, die diese Daten enthält. Auch hier variiert der Speicherort dieser Dateien je nach Betriebssystem. Am einfachsten ist es, die ausführliche Kompilierung in den Einstellungen zu aktivieren und zu überprüfen, wo die Ausgabedateien gespeichert werden.
Führen Sie den Befehl wie folgt aus:
avr-objdump -S output.elf> asm.txt
Und untersuchen Sie die Ausgabe in einem Texteditor.
main.c
von Arduino IDE verwendeten Standards ist . Dies bedeutet jedoch nicht, dass die HardwareSerial-Bibliothek in Ihrer Skizze enthalten ist. eigentlich ist es nicht enthalten , wenn Sie es nicht verwenden (das ist , warum gibt esif (serialEventRun)
inmain()
Funktion Wenn Sie nicht HardwareSerial Bibliothek verwenden dann.serialEventRun
null sein wird, damit kein Anruf.Die Antwort von Cybergibbons beschreibt die Assembler-Code-Generierung und die Unterschiede zwischen den beiden Techniken recht gut. Dies soll eine ergänzende Antwort sein, die sich mit dem Problem in Bezug auf praktische Unterschiede befasst, dh wie viel Unterschied beide Ansätze in Bezug auf die Ausführungszeit bewirken .
Code-Variationen
Ich habe eine Analyse mit folgenden Variationen durchgeführt:
void loop()
(wird beim Kompilieren eingeblendet)void loop()
(mit__attribute__ ((noinline))
)while(1)
(was optimiert wird)while(1)
(durch Hinzufügen__asm__ __volatile__("");
. Dies ist einenop
Anweisung, die die Optimierung der Schleife verhindert, ohne dass dies zu einem zusätzlichen Overhead einervolatile
Variablen führt.)void loop()
mit optimierten Inlinernwhile(1)
void loop()
mit einem Un-Optimiertenwhile(1)
Die Skizzen finden Sie hier .
Experiment
Ich habe jede dieser Skizzen 30 Sekunden lang ausgeführt und dabei jeweils 300 Datenpunkte gesammelt .
delay
In jeder Schleife gab es einen 100-Millisekunden- Aufruf (ohne den schlechte Dinge passieren ).Ergebnisse
Ich berechnete dann die mittleren Ausführungszeiten jeder Schleife, subtrahierte jeweils 100 Millisekunden und zeichnete dann die Ergebnisse auf.
http://raw2.github.com/AsheeshR/Arduino-Loop-Analysis/master/Figures/timeplot.png
Fazit
while(1)
Schleifevoid loop
ist schneller als eine vom Compiler optimiertevoid loop
.avr-gcc
wenn Sie manuell mithilfe Ihrer eigenen Optimierungsflags kompilieren und diese verwenden, als wenn Sie von der Arduino IDE abhängig sind, um dies zu unterstützen (wenn Sie Mikrosekundenoptimierungen benötigen).HINWEIS: Die tatsächlichen Zeitwerte sind hier nicht von Bedeutung, der Unterschied zwischen ihnen ist. Die ~ 90 Mikrosekunden der Ausführungszeit enthält einen Aufruf an
Serial.println
,micros
unddelay
.HINWEIS2: Dies wurde mit der Arduino IDE und den von ihr bereitgestellten Standard-Compiler-Flags durchgeführt.
ANMERKUNG 3: Die Analyse ( grafische Darstellung und Berechnungen) wurde unter Verwendung von R durchgeführt.
quelle