Да ускорим Arduino
/kn34pc.com/конструкции/...
Разлиствайки в "дебрите на интернет" за приложения с Arduino, случайно попаднах на тема: Calculation speed tables, Arduino Forum [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! Разликата е очевидна.
На всичко отгоре,
както в конкретни задачи, така и за посочения по-долу пример използвам
функцията за установяване в лог. 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 година