19

When you're writing a typical sketch, you usually rely on loop() being called repeatedly for as long as the Arduino is running. Moving in and out of the loop() function must introduce a small overhead though.

To avoid that, you could presumably create your own infinite loop, like this:

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

Is that a viable way to improve performance? Will it cause other problems if loop() never returns?

| improve this question | | | | |
18

The part of the code on an ATmega core that does setup() and loop() is at follows:

#include <Arduino.h>

int main(void)
{
        init();

#if defined(USBCON)
        USBDevice.attach();
#endif

        setup();

        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
        }

        return 0;
}

Pretty simple, but there is the overhead of the serialEventRun(); in there.

Let's compare two simple sketches:

void setup()
{

}

volatile uint8_t x;

void loop()
{

    x = 1;

}

and

void setup()
{

}

volatile uint8_t x;

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

The x and volatile is just to ensure it isn't optimised out.

In the ASM produced, you get different results: Comparison of two

You can see the while(true) just performs a rjmp (relative jump) back a few instructions, whereas loop() performs a subtraction, comparison and call. This is 4 instructions vs 1 instruction.

To generate ASM as above, you need to use a tool called avr-objdump. This is included with avr-gcc. Location varies depending on OS so it is easiest to search for it by name.

avr-objdump can operate on .hex files, but these are missing the original source and comments. If you have just built code, you will have a .elf file that does contain this data. Again, the location of these files varies by OS - the easiest way to locate them is to turn on verbose compilation in preferences and see where the output files are being stored.

Run the command as follows:

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

And examine the output in a text editor.

| improve this answer | | | | |
  • OK, but isn't there a reason for calling serialEventRun() function? What is it for? – jfpoilpret Feb 21 '14 at 17:18
  • 1
    It's part of the functionality used by HardwareSerial, not sure why it isn't taken out when Serial isn't needed. – Cybergibbons Feb 21 '14 at 17:29
  • 2
    Would be helpful to briefly explain how you generated the ASM output so people can check themselves. – jippie Feb 21 '14 at 19:41
  • @Cybergibbons it is never taken out because it is part of standard main.c used by Arduino IDE. However it does not mean that HardwareSerial library is included to your sketch; actually it is not included if you don't use it (that's why there is if (serialEventRun) in main() function. If you don't use HardwareSerial library then serialEventRun will be null, hence no call. – jfpoilpret Feb 21 '14 at 20:04
  • 1
    Yeah, it is part of the main.c as quoted, but I would expect it to be optimised out if not required hence I think aspects of Serial are always included. I frequently write code that will never return from loop() and don't notice issues with Serial. – Cybergibbons Feb 21 '14 at 21:24
6

Cybergibbons's answer describes quite nicely the assembly code generation and the differences amongst the two techniques. This is intended to be a complementary answer looking at the issue in terms of practical differences, i.e. how much of a difference either approach will make in terms of execution time.


Code Variations

I did an analysis involving the following variations:

  • Basic void loop() (which gets inlined on compilation)
  • Un-inlined void loop() (using __attribute__ ((noinline)))
  • Loop with while(1) (which gets optimized)
  • Loop with un-optimized while(1) (by adding __asm__ __volatile__("");. This is a nop instruction that prevents optimization of the loop without resulting in additional overheads of a volatile variable)
  • An un-inlined void loop() with optimized while(1)
  • An un-inlined void loop() with un-optimized while(1)

The sketches can be found here.

Experiment

I ran each of these sketches for 30 seconds, thereby accumulating 300 data points each. There was a 100 millisecond delay call in each loop (without which bad things happen).

Results

I then calculated the mean execution times of each loop, subtracted 100 milliseconds from each and then plotted the results.

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

Conclusion

  • An un-optimised while(1) loop within void loop is faster than a compiler optimised void loop.
  • The time difference between the un-optimized code and default Arduino optimized code is insignificant practically. You will be better off compiling manually using avr-gcc and using your own optimisation flags rather than depending on the Arduino IDE to help you with it (if you need microsecond optimisations).

NOTE: The actual time values are not of significance here, the difference between them is. The ~90 microseconds of execution time includes a call to Serial.println, micros and delay.

NOTE2: This was done using the Arduino IDE and the default compiler flags that it supplies.

NOTE3: Analysis (plot and calculations) was done using R.

| improve this answer | | | | |
  • 1
    Good work. Graph has milliseconds not microseconds but not a huge issue. – Cybergibbons Feb 22 '14 at 8:31
  • @Cybergibbons Thats quite unlikely since all measurements are in microseconds and I havent changed scales' anywhere :) – asheeshr Feb 22 '14 at 9:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.