การวนซ้ำไม่สิ้นสุดภายในลูป () จะทำงานเร็วขึ้นหรือไม่


19

เมื่อคุณเขียนแบบร่างโดยทั่วไปคุณจะต้องพึ่งพาloop()การถูกเรียกซ้ำ ๆ ตราบใดที่ Arduino ยังทำงานอยู่ การย้ายเข้าและออกจากloop()ฟังก์ชั่นจะต้องแนะนำค่าใช้จ่ายเล็กน้อย

เพื่อหลีกเลี่ยงปัญหานี้คุณอาจสร้างวงวนไม่สิ้นสุดของคุณเองเช่นนี้

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

นั่นเป็นวิธีที่ทำงานได้เพื่อปรับปรุงประสิทธิภาพหรือไม่ มันจะทำให้เกิดปัญหาอื่น ๆ ไหมถ้าloop()ไม่กลับมา?

คำตอบ:


18

ส่วนของรหัสบน ATmega core ที่ทำการเซ็ตอัพ () และลูป () มีดังต่อไปนี้:

#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 และค่าความผันผวนเป็นเพียงเพื่อให้แน่ใจว่าไม่ได้ปรับให้เหมาะสม

ใน ASM ที่ผลิตคุณจะได้รับผลลัพธ์ที่แตกต่าง: เปรียบเทียบสอง

คุณสามารถดู while (จริง) เพียงแค่ทำการ rjmp (การกระโดดแบบสัมพัทธ์) ย้อนกลับคำแนะนำบางส่วนในขณะที่ loop () ทำการลบการเปรียบเทียบและการโทร นี่คือ 4 คำแนะนำกับ 1 คำสั่ง

ในการสร้าง ASM ดังกล่าวคุณต้องใช้เครื่องมือที่เรียกว่า avr-objdump สิ่งนี้รวมอยู่ใน avr-gcc ตำแหน่งจะแตกต่างกันไปขึ้นอยู่กับระบบปฏิบัติการดังนั้นจึงง่ายที่สุดในการค้นหาด้วยชื่อ

avr-objdump สามารถทำงานกับไฟล์. hex ได้ แต่สิ่งเหล่านี้จะหายไปจากแหล่งและความคิดเห็นดั้งเดิม หากคุณเพิ่งสร้างรหัสคุณจะมีไฟล์. self ซึ่งมีข้อมูลนี้อยู่ อีกครั้งตำแหน่งของไฟล์เหล่านี้จะแตกต่างกันไปตามระบบปฏิบัติการ - วิธีที่ง่ายที่สุดในการค้นหาไฟล์คือการเปิดใช้งานการคอมไพล์อย่างละเอียดในการกำหนดค่าและดูตำแหน่งที่เก็บไฟล์เอาต์พุต

รันคำสั่งดังนี้:

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

และตรวจสอบผลลัพธ์ในเท็กซ์เอดิเตอร์


ตกลง แต่ไม่มีเหตุผลสำหรับการเรียกฟังก์ชัน serialEventRun () ใช่ไหม มีไว้เพื่ออะไร?
jfpoilpret

1
เป็นส่วนหนึ่งของฟังก์ชันที่ใช้โดย HardwareSerial ไม่แน่ใจว่าทำไมถึงไม่นำออกใช้เมื่อไม่จำเป็นต้องใช้อนุกรม
Cybergibbons

2
จะเป็นประโยชน์ในการอธิบายสั้น ๆ ว่าคุณสร้างเอาต์พุต ASM อย่างไรเพื่อให้ผู้คนสามารถตรวจสอบตัวเองได้
jippie

@Cybergibbons ไม่เคยถูกนำออกเพราะมันเป็นส่วนหนึ่งของมาตรฐานที่main.cใช้โดย Arduino IDE อย่างไรก็ตามไม่ได้หมายความว่าจะรวมไลบรารี่ของ HardwareSerial ไว้ในร่างของคุณ ที่จริงมันไม่ได้รวมอยู่ถ้าคุณไม่ได้ใช้มัน (ว่าทำไมมีif (serialEventRun)ในmain()ฟังก์ชั่นหากคุณไม่ได้ใช้ HardwareSerial ห้องสมุดแล้ว. serialEventRunจะเป็นโมฆะจึงไม่มีการโทร.
jfpoilpret

1
ใช่มันเป็นส่วนหนึ่งของ main.c ตามที่ยกมา แต่ฉันคาดหวังว่ามันจะได้รับการปรับให้เหมาะสมถ้าไม่จำเป็นดังนั้นฉันคิดว่าแง่มุมของ Serial จะรวมอยู่เสมอ ฉันมักเขียนโค้ดที่จะไม่ส่งคืนจากลูป () และไม่พบปัญหาเกี่ยวกับ Serial
Cybergibbons

6

คำตอบของ Cybergibbonsอธิบายการสร้างรหัสแอสเซมบลีและความแตกต่างระหว่างสองเทคนิคอย่างมาก นี้มีวัตถุประสงค์เพื่อเป็นคำตอบที่สมบูรณ์มองที่เป็นปัญหาในแง่ของการปฏิบัติที่แตกต่างกันคือวิธีการที่แตกต่างกันมากทั้งวิธีการที่จะทำให้ในแง่ของเวลาดำเนินการ


การเปลี่ยนแปลงรหัส

ฉันทำการวิเคราะห์ที่เกี่ยวข้องกับรูปแบบต่าง ๆ ต่อไปนี้:

  • พื้นฐานvoid loop()(ซึ่งได้รับการ inlined ในการรวบรวม)
  • ไม่มีการ inline void loop()(โดยใช้__attribute__ ((noinline)))
  • วนซ้ำด้วยwhile(1)(ซึ่งได้รับการเพิ่มประสิทธิภาพ)
  • วนซ้ำพร้อมกับยกเลิกการออปติไมซ์while(1)(โดยการเพิ่ม__asm__ __volatile__("");นี่เป็นnopคำสั่งที่ป้องกันการปรับให้เหมาะสมของลูปโดยไม่ส่งผลให้เกิดโอเวอร์เฮดของvolatileตัวแปรเพิ่มเติม)
  • แบบไม่มี inline 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.println, และ microsdelay

NOTE2:สิ่งนี้ทำโดยใช้ Arduino IDE และค่าสถานะเริ่มต้นคอมไพเลอร์ที่ให้

NOTE3: การวิเคราะห์ (แปลงและการคำนวณ) ดำเนินการโดยใช้ R


1
การทำงานที่ดี. กราฟมีมิลลิวินาทีไม่ใช่ไมโครวินาที แต่ไม่ใช่ปัญหาใหญ่
Cybergibbons

@Cybergibbons Thats ค่อนข้างน่าตั้งแต่วัดทั้งหมดอยู่ใน microseconds และ I havent เปลี่ยนได้ทุกเครื่องชั่งน้ำหนัก :)
asheeshr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.