loop()内的无限循环执行起来会更快吗?


19

在编写典型草图时,loop()只要Arduino运行,通常就需要反复调用。但是,移入和移出该loop()函数必须引入少量开销。

为了避免这种情况,您大概可以创建自己的无限循环,如下所示:

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

这是提高性能的可行方法吗?如果loop()永不返回,还会引起其他问题吗?

Answers:


18

ATmega内核上执行setup()和loop()的部分代码如下:

#include <Arduino.h>

int main(void)
{
        init();

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

        setup();

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

        return 0;
}

非常简单,但是有serialEventRun()的开销;在那里。

让我们比较两个简单的草图:

void setup()
{

}

volatile uint8_t x;

void loop()
{

    x = 1;

}

void setup()
{

}

volatile uint8_t x;

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

x和volatile只是为了确保未对其进行优化。

在生成的ASM中,您得到不同的结果: 两者的比较

您可以看到while(true)只是执行了rjmp(相对跳转)一些指令,而loop()则执行了减法,比较和调用。这是4条指令与1条指令。

要生成上述ASM,您需要使用一个名为avr-objdump的工具。这包含在avr-gcc中。位置因操作系统而异,因此最容易按名称搜索。

avr-objdump可以在.hex文件上运行,但是这些文件缺少原始来源和注释。如果您刚刚构建了代码,则将有一个包含此数据的.elf文件。同样,这些文件的位置因操作系统而异-找到它们的最简单方法是在首选项中打开详细编译,然后查看输出文件的存储位置。

运行命令,如下所示:

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

并在文本编辑器中检查输出。


可以,但是没有理由调用serialEventRun()函数吗?这是为了什么
jfpoilpret 2014年

1
它是HardwareSerial使用的功能的一部分,不确定是否在不需要串行时不将其删除。
Cyber​​gibbons 2014年

2
简要说明您如何生成ASM输出,以便人们可以检查自己会很有帮助。
jippie 2014年

@Cyber​​gibbons永远不会被淘汰,因为它是main.cArduino IDE使用的标准的一部分。但是,这并不意味着您的草图中包含了HardwareSerial库。实际上,如果你不使用它(它不包含这就是为什么有if (serialEventRun)main()功能如果不使用HardwareSerial库,然后,serialEventRun将是无效的,因此没有呼叫。
jfpoilpret

1
是的,它是所引用的main.c的一部分,但是我希望它可以在不需要的情况下得到优化,因此我认为Serial的各个方面都包括在内。我经常编写永远不会从loop()返回的代码,并且不会注意到Serial的问题。
Cyber​​gibbons 2014年

6

Cyber​​gibbons的答案很好地描述了汇编代码的生成以及这两种技术之间的差异。这旨在作为从实践上的差异来看问题的补充答案,即每种方法在执行时间方面将产生多少差异。


代码变体

我进行了涉及以下变化的分析

  • 基本void loop()(在编译时会内联)
  • 未内联void loop()(使用__attribute__ ((noinline))
  • 循环while(1)(已优化)
  • 未优化的循环while(1)(通过添加__asm__ __volatile__("");。这是一条nop指令,可防止在不导致volatile变量额外开销的情况下优化循环)
  • 未内联void loop()优化while(1)
  • 未内联且未void loop()优化while(1)

草图可以在这里找到。

实验

我将每个草图运行了30秒钟,从而每个积累了300个数据点delay每个循环中有一个100毫秒的调用(如果不发生这种情况,就会发生)。

结果

然后,我计算每个循环的平均执行时间,从每个循环中减去100毫秒,然后绘制结果。

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

结论

  • 未优化的while(1)循环void loop比编译器优化的循环要快 void loop
  • 实际上,未优化的代码和默认的Arduino优化的代码之间的时间差很小。您最好使用avr-gcc和使用自己的优化标志进行手动编译,而不是依赖Arduino IDE进行帮助(如果需要微秒优化)。

注意:此处的实际时间值不重要,它们之间的区别是。在〜90微秒的执行时间包括向呼叫Serial.printlnmicrosdelay

注意2:这是使用Arduino IDE及其提供的默认编译器标志完成的。

注3:使用R完成分析(绘图和计算)。


1
辛苦了 图有毫秒而不是微秒,但不是很大的问题。
Cyber​​gibbons

@Cyber​​gibbons那是不太可能的,因为所有的测量都以微秒为单位,而且我还没有在任何地方更改刻度:)
asheeshr 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.