Vraag:
Zou een oneindige lus binnen loop () sneller presteren?
Peter Bloomfield
2014-02-21 21:36:19 UTC
view on stackexchange narkive permalink

Als je een typische sketch schrijft, vertrouw je er meestal op dat loop () herhaaldelijk wordt aangeroepen zolang de Arduino draait. Het in- en verlaten van de loop () -functie moet echter een kleine overhead introduceren.

Om dat te voorkomen, zou je vermoedelijk je eigen oneindige lus kunnen creëren, zoals deze:

  void loop () {while (true) {// do stuff ...}}  

Is dat een haalbare manier om de prestaties te verbeteren? Zal het andere problemen veroorzaken als loop () nooit terugkeert?

Twee antwoorden:
#1
+18
Cybergibbons
2014-02-21 22:43:46 UTC
view on stackexchange narkive permalink

Het deel van de code op een ATmega-kern dat setup () en loop () uitvoert, is als volgt:

  #include <Arduino.h>int main (void) {init (); # indien gedefinieerd (USBCON) USBDevice.attach (); # endif setup (); for loop(); if (serialEventRun) serialEventRun (); } return 0;}  

Vrij eenvoudig, maar er is de overhead van de serialEventRun (); daarin.

Laten we twee eenvoudige schetsen vergelijken:

  void setup () {} vluchtige uint8_t x; void loop () {x = 1;}  

en

  void setup () {} vluchtige uint8_t x; void loop () {while (true) {x = 1; }}  

De x en vluchtig is alleen om ervoor te zorgen dat het niet wordt geoptimaliseerd.

In de geproduceerde ASM krijg je verschillende resultaten: Comparison of two

Je kunt zien dat while (true) gewoon een rjmp (relatieve sprong) terug een paar instructies uitvoert, terwijl loop () een aftrekking, vergelijking en call uitvoert. Dit zijn 4 instructies versus 1 instructie.

Om ASM te genereren zoals hierboven, moet je een tool genaamd avr-objdump gebruiken. Dit is inbegrepen bij avr-gcc. De locatie is afhankelijk van het besturingssysteem, dus het is het gemakkelijkst om ernaar te zoeken op naam.

avr-objdump kan werken met .hex-bestanden, maar deze missen de oorspronkelijke bron en opmerkingen. Als je net code hebt gebouwd, heb je een .elf-bestand dat deze gegevens wel bevat. Nogmaals, de locatie van deze bestanden verschilt per besturingssysteem - de gemakkelijkste manier om ze te vinden is door uitgebreide compilatie in voorkeuren in te schakelen en te kijken waar de uitvoerbestanden worden opgeslagen.

Voer de opdracht als volgt uit:

avr-objdump -S output.elf> asm.txt

En bekijk de output in een teksteditor.

OK, maar is er geen reden om de functie serialEventRun () aan te roepen? Waar is het voor?
Het maakt deel uit van de functionaliteit die wordt gebruikt door HardwareSerial, ik weet niet zeker waarom het niet wordt verwijderd als Serial niet nodig is.
Het zou nuttig zijn om kort uit te leggen hoe u de ASM-uitvoer hebt gegenereerd, zodat mensen zichzelf kunnen controleren.
@Cybergibbons het wordt nooit verwijderd omdat het deel uitmaakt van de standaard `main.c` die wordt gebruikt door Arduino IDE. Het betekent echter niet dat de HardwareSerial-bibliotheek bij uw schets is opgenomen; eigenlijk is het niet inbegrepen als je het niet gebruikt (daarom is er `if (serialEventRun)` in `main ()` functie. Als je geen HardwareSerial bibliotheek gebruikt, zal `serialEventRun` null zijn, dus geen aanroep .
De HardwareSerial-bibliotheek wordt echter waarschijnlijk opgenomen (kan iemand dat bevestigen?) Als je arduino-functies zoals `Serial.print ()` gebruikt. Wat kan er in deze situatie gebeuren als uw `loop ()` voor altijd blijft bestaan ​​en dus nooit `serialEventRun ()` aanroept? Ik ben bang dat Serial in dit geval niet werkt.
Ja, het maakt deel uit van de main.c zoals geciteerd, maar ik zou verwachten dat het wordt geoptimaliseerd, zo niet vereist, daarom denk ik dat aspecten van Serial altijd zijn inbegrepen. Ik schrijf vaak code die nooit terugkeert van loop () en merk geen problemen met Serial.
@Cybergibbons, `serialEventRun ()` roept `serialEvent ()` aan voor de verschillende seriële poorten. Standaard zijn dit allemaal [lege procedures] (https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/HardwareSerial.cpp), maar ze kunnen worden [overschreven door de gebruiker ] (http://arduino.cc/en/Tutorial/SerialEvent) om desgewenst iets nuttigs te doen. Als u het niet opheft, hoeft u het niet te bellen.
#2
+6
asheeshr
2014-02-22 14:11:34 UTC
view on stackexchange narkive permalink

Cybergibbons's antwoord beschrijft heel mooi het genereren van assemblagecodes en de verschillen tussen de twee technieken. Dit is bedoeld als een aanvullend antwoord dat de kwestie bekijkt in termen van praktische verschillen, dwz hoeveel verschil een van beide benaderingen zal maken in termen van uitvoeringstijd .


Codevariaties

Ik heb een analyse uitgevoerd met de volgende variaties:

  • Basic void loop ( ) (die wordt inline bij compilatie)
  • Niet-inline void loop () (met __attribute__ ((noinline)) )
  • Loop met while (1) (die wordt geoptimaliseerd)
  • Loop met niet-geoptimaliseerde while (1) (door toe te voegen __asm__ __volatile __ (""); . Dit is een nop -instructie die optimalisatie van de lus voorkomt zonder te resulteren in extra overheadkosten van een vluchtige -variabele)
  • Een niet-inline leegte-lus () met geoptimaliseerde terwijl(1)
  • Een niet-inline leegte loop () met niet-geoptimaliseerde while(1)

De schetsen kunnen worden gevonden d hier.

Experiment

Ik heb elk van deze schetsen 30 seconden lang uitgevoerd, waarbij ik elk 300 gegevenspunten verzamelde. Er was een vertraging -aanroep van 100 milliseconden in elke lus (zonder welke erge dingen gebeuren).

Resultaten

Vervolgens heb ik de gemiddelde uitvoeringstijden van elke lus berekend, 100 milliseconden afgetrokken van elke lus en vervolgens de resultaten geplot.

http://raw2.github.com/AsheeshR/Arduino-Loop-Analysis/master/Figures/timeplot.png

Conclusie

  • Een niet-geoptimaliseerde while (1) -lus binnen de void-lus is sneller dan een door de compiler geoptimaliseerde void-lus .
  • Het tijdsverschil tussen de niet-geoptimaliseerde code en de standaard Arduino-geoptimaliseerde code is praktisch onbeduidend. U kunt beter handmatig compileren met avr-gcc en uw eigen optimalisatievlaggen gebruiken in plaats van afhankelijk te zijn van de Arduino IDE om u hierbij te helpen (als u optimalisaties van microseconden nodig heeft).

OPMERKING: de werkelijke tijdwaarden zijn hier niet van belang, het verschil tussen beide is dat wel. De ~ 90 microseconden aan uitvoeringstijd omvat een aanroep naar Serial.println , micros en vertraging.

NOTE2: Dit werd gedaan met behulp van de Arduino IDE en de standaard compilervlaggen die het levert.

NOTE3: Analyse (plot en berekeningen) werd gedaan met R.

Goed werk. De grafiek heeft milliseconden, geen microseconden, maar geen groot probleem.
@Cybergibbons Dat is vrij onwaarschijnlijk omdat alle metingen in microseconden zijn en ik de schaal nergens heb gewijzigd :)


Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...