Чи буде нескінченна петля всередині циклу () виконувати швидше?


19

Коли ви пишете типовий ескіз, ви зазвичай покладаєтесь на loop()те, щоб вас дзвонили повторно протягом тих пір, поки працює Arduino. Якщо ввійти та вийти з loop()функції, потрібно ввести невеликі накладні витрати.

Щоб цього уникнути, ви, мабуть, можете створити свій власний нескінченний цикл, наприклад:

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

Це життєздатний спосіб поліпшити продуктивність? Чи це спричинить інші проблеми, якщо loop()ніколи не повернеться?

Відповіді:


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 і мінливі - це лише гарантувати його оптимізацію.

У виробництві ASM ви отримуєте різні результати: Порівняння двох

Ви можете бачити, що час (true) просто виконує rjmp (відносний стрибок) назад кілька інструкцій, тоді як loop () виконує віднімання, порівняння та виклик. Це 4 інструкції проти 1 інструкції.

Щоб генерувати ASM, як зазначено вище, вам потрібно використовувати інструмент під назвою avr-objdump. Це включено до avr-gcc. Розташування змінюється в залежності від ОС, тому його найлегше шукати за назвою.

avr-objdump може працювати з файлами .hex, але в них відсутні оригінальне джерело та коментарі. Якщо ви тільки що створили код, у вас буде файл .elf, який містить ці дані. Знову ж таки, розташування цих файлів залежить від ОС - найпростіший спосіб їх пошуку - увімкнути довільну компіляцію в налаштуваннях і побачити, де зберігаються вихідні файли.

Виконайте команду наступним чином:

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

І вивчіть вихід у текстовому редакторі.


Гаразд, але чи немає причини викликати функцію serialEventRun ()? Навіщо це?
jfpoilpret

1
Це частина функціоналу, який використовується HardwareSerial, не впевнений, чому він не виймається, коли послідовність не потрібна.
Кібергіббони

2
Було б корисно коротко пояснити, як ви створили вихід ASM, щоб люди могли перевірити себе.
jippie

@Cybergibbons його ніколи не вивозять, тому що він є частиною стандарту, який main.cвикористовується Arduino IDE. Однак це не означає, що бібліотека HardwareSerial включена у ваш ескіз; на самому ділі це не входить , якщо ви не використовуєте його (саме тому існує if (serialEventRun)в main()функції Якщо ви не використовуєте HardwareSerial бібліотека тоді. serialEventRunбуде нульовим, отже , немає виклику.
jfpoilpret

1
Так, це частина main.c, як цитується, але я б очікував, що вона буде оптимізована, якщо не потрібна, тому я думаю, що аспекти Serial завжди включені. Я часто пишу код, який ніколи не повернеться з циклу () і не помічаю проблем із Serial.
Кібергіббони

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 точок даних кожен . Здійснювався 100 мілісекундний delayдзвінок у кожному циклі (без якого погані речі не трапляються ).

Результати

Потім я обчислював середні часи виконання кожного циклу, віднімав 100 мілісекунд від кожного, а потім намітив результати.

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

Висновок

  • Неоптимізований while(1)цикл всередині void loopшвидше, ніж оптимізований компілятор void loop.
  • Різниця в часі між неоптимізованим кодом та оптимізованим кодом Arduino за замовчуванням практично не суттєва . Вам буде краще компілювати вручну, використовуючи avr-gccі використовуючи власні прапори оптимізації, а не залежно від ID Arduino, щоб допомогти вам у цьому (якщо вам потрібні мікросекундні оптимізації).

ПРИМІТКА . Фактичні значення часу тут не мають значення, різниця між ними. В ~ 90 мікросекунд часу виконання включає в себе виклик Serial.println, microsі delay.

ПРИМІТКА2. Це було зроблено за допомогою Arduino IDE та прапорів компілятора за замовчуванням, які він постачає.

ПРИМІТКА 3. Аналіз (графік та обчислення) проводили за допомогою Р.


1
Хороша робота. Графік має мілісекунди не мікросекунди, але не є великою проблемою.
Кібергібсони

@Cybergibbons Це малоймовірно, оскільки всі вимірювання проводяться в мікросекундах, і я нікуди не міняв шкалу :)
asheeshr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.