लूप के अंदर एक अनंत लूप () तेजी से प्रदर्शन करेगा?


19

जब आप एक ठेठ स्केच लिख रहे होते हैं, तो आप आमतौर पर loop()अरुदिनो के चलने तक बार-बार कॉल करने पर निर्भर होते हैं । loop()हालांकि फंक्शन में अंदर और बाहर जाना एक छोटे ओवरहेड को पेश करता है।

इससे बचने के लिए, आप संभवतः अपना स्वयं का अनंत लूप बना सकते हैं, जैसे:

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

क्या यह प्रदर्शन में सुधार करने का एक व्यवहार्य तरीका है? loop()कभी न लौटने पर क्या यह अन्य समस्याओं का कारण होगा ?

जवाबों:


18

एक ATmega कोर पर कोड का हिस्सा जो सेटअप () और लूप () निम्नानुसार है:

#include <Arduino.h>

int main(void)
{
        init();

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

        setup();

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

        return 0;
}

बहुत आसान है, लेकिन धारावाहिक के ओवरहेडरून () है; वहाँ पर।

आइए दो सरल रेखाचित्रों की तुलना करें:

void setup()
{

}

volatile uint8_t x;

void loop()
{

    x = 1;

}

तथा

void setup()
{

}

volatile uint8_t x;

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

एक्स और अस्थिर यह सुनिश्चित करने के लिए है कि इसे बाहर अनुकूलित नहीं किया गया है।

उत्पादित ASM में, आपको अलग-अलग परिणाम मिलते हैं: दो की तुलना

आप देख सकते हैं जबकि (सच्चा) सिर्फ एक आरजेएमपी (सापेक्ष कूद) कुछ निर्देशों को करता है, जबकि लूप () एक घटाव, तुलना और कॉल करता है। यह 4 निर्देश बनाम 1 निर्देश है।

ऊपर के रूप में ASM उत्पन्न करने के लिए, आपको avr-objdump नामक टूल का उपयोग करना होगा। यह avr-gcc के साथ शामिल है। स्थान OS पर निर्भर करता है इसलिए नाम से इसे खोजना सबसे आसान है।

avr-objdump .hex फ़ाइलों पर काम कर सकता है, लेकिन ये मूल स्रोत और टिप्पणियों को याद नहीं कर रहे हैं। यदि आपने अभी-अभी कोड बनाया है, तो आपके पास एक .elf फ़ाइल होगी जिसमें यह डेटा होता है। फिर से, इन फ़ाइलों का स्थान ओएस द्वारा भिन्न होता है - उन्हें खोजने का सबसे आसान तरीका वरीयताओं में क्रिया संकलन चालू करना है और देखें कि आउटपुट फ़ाइलों को कहाँ संग्रहीत किया जा रहा है।

निम्नानुसार कमांड चलाएँ:

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

और एक टेक्स्ट एडिटर में आउटपुट की जांच करें।


ठीक है, लेकिन क्या serialEventRun () फ़ंक्शन को कॉल करने का कोई कारण नहीं है? ये किसके लिये है?
jfpoilpret

1
यह HardwareSerial द्वारा उपयोग की जाने वाली कार्यक्षमता का हिस्सा है, यह सुनिश्चित नहीं है कि जब सीरियल की जरूरत नहीं है तो इसे बाहर क्यों नहीं निकाला जाए।
Cybergibbons

2
संक्षिप्त रूप से यह समझाने में मददगार होगा कि आपने ASM आउटपुट कैसे उत्पन्न किया ताकि लोग खुद की जाँच कर सकें।
जिप्पी

@Cybergibbons इसे कभी भी बाहर नहीं निकाला जाता क्योंकि यह main.cArduino IDE द्वारा उपयोग किए जाने वाले मानक का हिस्सा है । हालांकि इसका मतलब यह नहीं है कि हार्डवेयरस् लाइब्रेरी आपके स्केच में शामिल है; वास्तव में यह शामिल नहीं है यदि आप इसका उपयोग नहीं करते हैं (यही कारण है if (serialEventRun)कि main()फ़ंक्शन में है। यदि आप HardwareSerial लाइब्रेरी का उपयोग नहीं करते हैं serialEventRun, तो यह शून्य हो जाएगा, इसलिए कोई कॉल नहीं।
jfpoilpret

1
हाँ, यह उद्धृत मुख्य main.c का हिस्सा है, लेकिन मुझे उम्मीद है कि यदि आवश्यक नहीं है तो मुझे इसे अनुकूलित करना होगा क्योंकि मुझे लगता है कि सीरियल के पहलू हमेशा शामिल होते हैं। मैं अक्सर कोड लिखता हूं जो लूप से कभी नहीं लौटेगा () और सीरियल के साथ मुद्दों पर ध्यान नहीं देगा।
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-gccयदि आप इसके साथ मदद करने के लिए Arduino IDE पर निर्भर करते हैं (तो आपको माइक्रोसेकंड ऑप्टिमाइज़ेशन की आवश्यकता है) के बजाय मैन्युअल रूप से अपने स्वयं के अनुकूलन झंडे का उपयोग करने और उपयोग करने से बेहतर होगा ।

नोट: वास्तविक समय मान यहाँ महत्व के नहीं हैं, उनके बीच का अंतर है। निष्पादन समय के ~ 90 माइक्रोसेकंड में कॉल टू Serial.println, microsऔर शामिल है delay

NOTE2: यह Arduino IDE और डिफ़ॉल्ट कंपाइलर झंडे का उपयोग करके किया गया था जो इसकी आपूर्ति करता है।

NOTE3: विश्लेषण (साजिश और गणना) आर का उपयोग करके किया गया था।


1
अच्छा कार्य। ग्राफ में मिलीसेकंड नहीं माइक्रोसेकंड नहीं बल्कि एक बड़ा मुद्दा है।
Cybergibbons

@Cybergibbons सभी माप microseconds में कर रहे हैं और मुझे लगता है कि 'कहीं भी तराजू बदल दिया :) की संभावना नहीं है
asheeshr
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.