loop () 내부의 무한 루프가 더 빨리 수행됩니까?


19

일반적인 스케치를 작성할 때는 일반적으로 loop()Arduino가 실행되는 동안 반복적으로 호출되는 것에 의존 합니다. loop()함수 로 들어오고 나가는 것은 작은 오버 헤드를 가져와야합니다.

이를 피하기 위해 다음과 같이 자신 만의 무한 루프를 만들 수 있습니다.

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

이것이 성능을 향상시키는 실용적인 방법입니까? loop()돌아 오지 않으면 다른 문제가 발생 합니까?

답변:


18

setup () 및 loop ()를 수행하는 ATmega 코어의 코드 부분은 다음과 같습니다.

#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에 포함되어 있습니다. 위치는 OS에 따라 다르므로 이름별로 검색하는 것이 가장 쉽습니다.

avr-objdump는 .hex 파일에서 작동 할 수 있지만 원래 소스와 주석이 없습니다. 방금 코드를 빌드 한 경우이 데이터가 포함 된 .elf 파일이 생성됩니다. 이 파일의 위치는 OS에 따라 다릅니다. 파일을 찾는 가장 쉬운 방법은 환경 설정에서 상세 컴파일을 켜고 출력 파일이 저장되는 위치를 확인하는 것입니다.

다음과 같이 명령을 실행하십시오.

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

그리고 텍스트 편집기에서 출력을 검사하십시오.


그래도 serialEventRun () 함수를 호출 할 이유가 없습니까? 무엇입니까?
jfpoilpret

1
그것은 SerialSerial이 필요하지 않을 때 왜 꺼내지지 않는지 HardwareHerial이 사용하는 기능의 일부입니다.
Cybergibbons

2
사람들이 스스로 확인할 수 있도록 ASM 출력을 생성 한 방법을 간단히 설명하면 도움이됩니다.
jippie

@Cybergibbons main.cArduino IDE에서 사용하는 표준의 일부이므로 절대로 꺼내지 않습니다 . 그러나 HardwareSerial 라이브러리가 스케치에 포함 된 것은 아닙니다. 실제로 사용하지 않으면 포함되지 않습니다 ( 기능 이 if (serialEventRun)있는 이유 main()입니다. HardwareSerial 라이브러리를 사용하지 않으면 serialEventRunnull이 발생하므로 호출이 없습니다.
jfpoilpret

1
예, 인용 된 main.c의 일부이지만 필요하지 않으면 최적화 될 것으로 기대합니다. 따라서 Serial의 측면이 항상 포함되어 있다고 생각합니다. 나는 loop ()에서 결코 리턴되지 않는 코드를 자주 작성하며 Serial 관련 문제를 인식하지 못합니다.
Cybergibbons

6

Cybergibbons의 답변 은 어셈블리 코드 생성과 두 기술의 차이점을 잘 설명합니다. 이것은 실제적인 차이, 즉 두 접근 방식이 실행 시간 과 관련하여 얼마나 많은 차이를 보일지 에 대한 문제를 검토하는 보완적인 답변 입니다.


코드 변형

나는 한 분석을 다음과 같은 변화를 수반을 :

  • 기본 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-gccArduino IDE에 의존하지 않고 직접 최적화 플래그를 사용 하고 수동으로 컴파일하는 것이 좋습니다 (마이크로 초 최적화가 필요한 경우).

참고 : 실제 시간 값은 여기에서 중요하지 않습니다. 실행 시간의 ~ 90 마이크로 초에 대한 호출을 포함하고 Serial.println, micros하고 delay.

NOTE2 : 이것은 Arduino IDE와 그것이 제공하는 기본 컴파일러 플래그를 사용하여 수행되었습니다.

NOTE3 : 분석 (플롯 및 계산)은 R을 사용하여 수행되었습니다.


1
잘 했어. 그래프는 밀리 초는 마이크로 초가 아니지만 큰 문제는 아닙니다.
Cybergibbons

@Cybergibbons 모든 측정이 마이크로 초 단위이고 스케일을 변경하지 않았기 때문에 그럴 가능성은 거의 없습니다. :)
asheeshr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.