Да ускорим Arduino

    Разлиствайки в "дебрите на интернет" за приложения с Arduino, случайно попаднах на тема: Calculation speed tables, Arduino Forum [1], която остави в мен дълбока следа за анализа на алгоритмите, използваните функции и променливи в бъдещите ми ардуино-програми.


Табл. 1

    Графите са на пръв поглед стряскащи в разликата по параметър: време / брой инструкции за изпълнение в MCU.

    Разглеждам лесно сравнимите, например:

    Arduino commands: Digital write (pin witout PWM) и Assembly commands: Digital write to 8 pins

    3,93 µs / 63 CPU cycles срещу 0,06 µS / 1 CPU Cycle! Разликата е очевидна.


Табл. 2


Табл. 3

     На всичко отгоре, както в конкретни задачи, така и за посочения по-долу пример използвам функцията за установяване в лог. 1 (и след това: в лог. 0) не на един, а на шест изхода (D2, D3, D4, D5, D6, D7) и Arduino командата трябва да се изпълни по шест пъти, а с Assembly функцията това се извършва един път всички 8 изхода на port D, т.e без разглежданите допълнително функции по установяване на цикъл, сравнение, доп. лог. функции самото действие по време в единият случай ще заема

    6 * 3,93 µs = 23,58 µs / 378 cycles срещу 0,06 µS / 1 сycle за едно и също действие.
   
    Измерването така или иначе е лесния вариант на бегъл анализ, без значение на това, че e емпиричен и стойностите зависят пряко от входните условия.

    За моя тестова задача си поставих просто установяване в лог. 1 ("светване" на светодиоди) и последващо установяване в лог. 0 ("загасяне" на светодиоди) на всички изходи в един порт (в случая с изходи D2, D3, D4, D5, D6 и D7 / Port D), като не манипулирам по никакъв начин бит 0 и бит 1 (D0 и D1 / RX/TX), отговорни за комуникацията в Arduino.

    Резултатите са достъпни и без свързване на реални светодиоди в тестовата установка (скоростта на примигване е висока ), но за нагледност начертах и електрическата схема.



Архив: arduino_speed_test.zip [zip,ino,spl7,gif][43kb]

    За измерване на време използвам серийния монитор: преди изпълнението отчитам времето при началото на стартиране на програмата, а след действието: времето при край на изпълнение. Разликата между двете времена е "почти" времето на изпълнение.

    Понеже действието е в микросекундната област, повторих го многократно (1М пъти) за видимото отчитане в разликите и избягване на случайни грешки.

    Предварително се застраховам от ръчно изчислените времена, скорости и сумарните им стойности: кодът в Arduino допълнително се оптимизира от GCC compiler на Atmel AVR microcontrollers. Какво се случва след него за мен е непредвидимо (и понякога води до интересни резултати: примерно видимо "къс" код се изпълнява по-бавно в сравнение с "по-дългия" такъв за едно и също действие върху еднакъв тип данни).

    Приложил съм варианти на програмката с Arduino функции и директно писане в портовете (Arduino commands / direct port manipulation), както и използване на променливи с "неправилно" избран тип според изменението на входните данни (long / float). За мое учудване имаше разлика и в командите за установяване на цикъл (for {}, while {}, do {} while {}).

    Да обобщим: при използвано Arduino Nano, захранено с 5V, осцилатор с керамичен резонатор на 16 MHz:

тест: команди: цикъл: брояч:

време: µS

Тест 1 Arduino commands for {} long counter    50241156 
Тест 2 direct port manipulation for {} long counter    2578080 
Тест 3 Arduino commands while {} float counter    61297696 
Тест 4 direct port manipulation do {} while {} float counter    13068696 
Тест 5 direct port manipulation do {} while {} long counter    2012164 
Тест 6 direct port manipulation loop {} long counter    3144004 

    За 1 000 000 действия:
    Най-бърз тест: Тест 5 - 2012164 µS
    Най-бавен тест: Тест 3 - 61297696 µS

    или при използване на "бързия" метод:
 
    1. за единица време Arduino ще изпълни примерно 30 пъти повече операции или 2. определен брой операции ще се изпълнят 30 пъти по-бързо. Това без никаква промяна в конфигурацията на системката. Мисля, че "печалбата" си заслужава!

    С друг модул Arduino (заради малките отклонения в честотите на керамичните (не кварцови) резонатори?) времената предполагам ще са близки до моите, но порядъкът в отношението ще е същия.

    След очевидните разлики във времената за изпълнение на едни и същи действия, върху едни и същи входни данни, но алгоритъм на "различен език" към Arduino започвам да разбирам "усмивката под мустак" за работата на Arduino, коментирана в разговори с мои приятели и колеги, дълго занимаващи се с "чисти" микропроцесорни системи, пишещи код на C, Assembler и др.

    Лично за себе си знам, че няма да променя съществено навиците си на писане с Ардуиновските команди и функции (които са ми по-близки до човешкото мислене), но поне ще правя опити за оптимизация и писане на езици на по-ниско ниво в конкретни ситуации, свързани с времена на четене и запис на бързи процеси по входове и изходи, директно генериране сигнали с висока честота и др.
 

Използвани материали: 

1. Calculation speed tables, Arduino Forum

LZ2WSG, KN34PC
3 ноември 2016 година