Апгрейд микроконтроллерных отладочных плат для любителей - шаг назад?



Здравствуйте, уважаемые читатели! Свыше десяти лет назад - в далёком 2005 году был начат выпуск Arduino - простейшей и недорогой отладочной платы, предназначенной для любительского конструирования микропроцессорных устройств. Arduino представляла собой небольшую печатную плату с микроконтроллером ATMega328 и разъёмами цифровых и аналоговых портов, программирования и питания. Специально для Arduino была разработана простая среда разработки программ, состоящая из текстового редактора, компилятора С++ и утилиты прошивки микроконтроллера. И всё! А больше и не нужно - просто и понятно.




Это был настоящий прорыв - для создания своей микропроцессорной конструкции не обязательно было изготавливать печатную плату, приобретать или собирать программатор, не нужно изучать архитектуру ассемблер микроконтроллера - программа пишется на языке высокого уровня. Конечно, за предельную простоту разработки пришлось очень здорово платить производительностью - она упала почти на порядок, но благодаря передовому на тот момент микроконтроллеру, остатков вычислительной мощи с запасом хватало для большинства любительских задач. Я думаю, не надо говорить, что Arduino быстро приобрела огромную популярность по всему миру. Со временем, появились новые варианты плат Arduino, с более мощным микроконтроллером, большим количеством портов... Arduino прочно и надолго заняла нишу первых шагов в мир программирования микроконтроллеров.


Технический прогресс не стоит на месте, и в 2011 году появился Raspberry Pi, уже не просто отладочная плата, а целый микрокомпьютер! Raspberry Pi был построена на ARM11 процессоре Broadcom BCM2835 стактовой частотой 700 МГц и модулем оперативной памяти на 256/512 МБ. Raspberry Pi был предназначен для обучения студентов и школьников программированию, но также как и Arduino приобрёл огромную популярность у любителей. Да, это был второй прорыв в мир программирования, на этот раз уже компьютеров на Linux. Главное, отлично сработала получившаяся преемственность устройств - любитель, имеющий опыт программирования Arduino мог использовать накопленные наработки для программирования микрокомпьютера Raspberry Pi. Вместо восьмибитного 16 МГц микроконтроллера с 2 кб ОЗУ любитель получал 700 МГц ARM1176JZ-F процессор с 256/512 Мб ОЗУ и операционной системой Raspbian на основе Linux, по производительности сравнимый с компьютером на основе Pentium II 300 MHz. Для программирования на борту Raspberry Pi имелись две версии интерпретатора Питон - Phyton 2 и Phyton 3.




К сожалению, прогресс начал сводить на нет применимость любительской технологии. Если любитель по каким-то причинам хотел повторить Arduino, он спокойно изготавливал самодельную двустороннюю плату и запаивал в неё микроконтролер с необходимыми радиодеталями. Такое поделие хоть и могло выглядеть непрезентабельно, но было вполне работоспособно и оно работало. А вот повторение Raspberry Pi для любителя уже стало недостижимо - изготовить четырёхслойную плату можно только в заводских условиях, а запаивать компоненты BGA монтажа придётся как минимум с помощью специальной инфракрасной паяльной станции.


Да, техника здорово шагнула вперёд, даже в России не остались равнодушными и выпустили свой микрокомпьютер МВ77.07 на российском телевизионном процессоре К1879ХБ1Я с точно таким же ядром ARM1176JZ-F, правда, с вдвое меньшей тактовой частотой - всего 324 МГц вместо 700 МГц Raspberry Pi, зато с дополнительным ядром нейропроцессора NMC3 российской разработки! Для российского МВ77.07 были портированы образы Raspbian и на нём работали программы, предназначенные для Raspberry Pi.




Вот оно, наконец-то наступило светлое будущее! Компьютеризация любительских поделок достигла сияющих вершин! Любитель, освоивший Arduino, с приобретением Raspberry Pi делал стремительный рывок вперёд. Старая самодельная метеостанция на Arduino спешно переделывалась под подключённый к интернету Raspberry Pi. Счастливый любитель после апгрейда уже мог прямо на работе со смартфона наблюдать за погодой в доме, и на основе тревожных сводок о стремительно повышающейся влажности, с того же смартфона оперативно вызвать бригаду ремонтников заливающим квартиру соседям.


Какие преимущества любителю даёт переход с Arduino на Raspberry Pi в качестве управляющего устройства? Однозначно, удобство работы - в отличие от отладочной платы микрокомпьютер без каких-либо наворотов можно сразу подключить к интернету и управлять им хоть через полмира! А как же быстродействие? Поскольку, для большинства любительских задач вычислительных мощностей Arduino хватает с запасом, не говоря о Raspberry Pi, то сразу трудно оценить. Ведь в работе переделанной с Arduino на Raspberry Pi метеостанции, кроме удалённого доступа или может сохраняемого журнала работы, ничего не поменялось - как работала, так и работает. Так у какого аппарата будет большее быстродействие - у Arduino с компилятором С++ или Raspberry Pi с интерпретатором Python 2/3? Конечно же на Raspberry Pi... на порядок возросшая вычислительная мощь процессора наверняка перекроет принципиальный недостаток интерпретатора - отсутствие оптимизации? А может, старенькая Arduino не ударит в грязь лицом? Давайте проверим.


Нужен какой-то простой и в то же время наглядный тест производительности. Миллион пустых циклов? Нет, слишком простой. Девять ферзей? Недостаточная сложность, время проверки заведомо не превысит секунды. Тогда остаётся в качестве теста использовать расчёт количества "счастливых" билетов по алгоритму, предложенным Сергеем Тарасовым на сайте Кон-Тики - полный перебор всего миллиона вариантов и подсчётом попадающихся "счастливых" экземпляров. А почему "счастливых"? Есть старая шуточная примета - если в шестизначном номере автобусного, троллейбусного или трамвайного билета сумма цифр первой половины номера совпадает с суммой цифр второй половины номера, значит билет "счастливый" и можно смело загадывать желание.




Отлично, заставим считать имеющиеся у меня отладочные платы и микрокомпьютеры считать "счастливые" билеты. У меня как раз есть Arduino UNO на ATMega328P, но Raspberry Pi у меня в наличии нет, вместо него тестовые программы будет выполнять русский микрокомпьютер МВ77.07 на К1879ХБ1Я, благо у него такое же процессорное ядро - ARM1176JZ-F. Микроконтроллер ATMega328P построен на базе ядра AVR с производительностью 1 млн.оп./с и работает на частоте 16 МГц, а значит быстродействие у него - 16 млн.оп./с. Микросхема К1879ХБ1Я имеет ядро ARM1176JZ-F с производительностью 1,25 млн.оп./с и работает на частоте 324 МГц - быстродействие 405 млн.оп./с. На первый взгляд - явное преимущество в 25 раз, а если сравнивать с BCM2835 с частотой 700МГц, то и вовсе разница составляет почти 55 раз... А что же покажет эксперимент?


Итак, начнём. Первой будет плата Arduino UNO и её адаптированный к любительскому программированию компилятор С++.


int led = 13;
byte a;
byte b;
byte c;
byte d;
byte e;
byte f;
word g;
boolean h=false;
byte i;
byte j;
byte k;
void setup() {
pinMode(led, OUTPUT);
}
void loop()
{
g=0;
for (a=10; a=1; a--) {
for (b=10; b=1; b--) {
i=a+b;
for (c=10; c=1; c--) {
j=i+c;
for (d=10; d=1; d--) {
for (e=10; e=1; e--) {
k=d+e;
for (f=10; f=1; f--) {
if (j==(k+f)) g++;
}
}
}
}
}
}
if (g==55252)
h=!h;
digitalWrite(led, h);
}


Результат - 0,78 секунд! Я был сильно изумлён, ведь тест миллиона пустых циклов на Arduino UNO у меня выполнялся целых 6 секунд. Давайте разберём эти тесты подробно и рассчитаем количество выполняемых операций. Тест миллион пустых циклов предельно прост:


for (f=1000000; f=1; f--)

и содерджит три операции - декремент, проверку условия и переход. Итого тест занимает три миллиона операций и выполняется за шесть секунд - итого быстродействие полмиллиона операций в секунду. Эффективность компилятора равна результирующему быстродействию, делённая на быстодействие самого процессора, это буквально коэффициент полезного действия. Для миллиона пустых циклов эффективность компилятора равна 0,5 млн.оп./с разделить на 16 млн.оп./с, итого - 3,1%. Весьма негусто. А что же с подсчётом "счастливых" билетов? Тест содержит шесть циклов, вложенных друг в друга. На выполнение самого внутреннего цикла


for (f=10; f=1; f--)
{
if (j==(k+f)) g++;
}


тратится 90% процессорного времени. Поэтому, затраты на цикл можно считать за три операции, проверку условия, декремент и переход. Если учесть что количество счастливых билетов всего 5,5% от всего количества (55252), то затратами на инкремент счётчика можно пренебречь, поэтому затраты на проверку можно считать за три операции, сложение, сравнение и переход. Всего 6 операций, + 1 операция на внешние циклы (6/10) и подсчёт билетов. Всего миллион циклов, поэтому можно считать что выполнение теста занимает 7 миллионов операций. Итого быстродействие около 9 млн. оп./с. Эффективность 9/16 = 56%. Вот это да! Налицо оптимизация скомпилированой программы - результирующее быстродействие выходит на уровне ассемблера! Программа честно сравнивает полученный результат с 55252 шт. "счастливых" билетов, наверняка компилятор просто "развернул" внутренний цикл.


Теперь пора протестировать эффективность интерпретаторов для Raspberry Pi на русском микрокомпьютере МВ77.07. У меня есть целая коллекция: - php, ruby, perl, python 2 и python 3. Поскольку микрокомпьютером управляет многозадачная операционная система, а приоритет скриптовых интерпретаторов средний, то на результаты может повлиять запуск системных процессов во время теста. Поэтому, для чистоты эксперимента тесты будут запускаться по пять раз, а полученные значения усредняться.


Начну с интерпретатора веб-скриптов php5-fpm 5.6.38:


?рhp
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
$a=0;
$b=0;
$c=0;
$d=0;
$e=0;
$f=0;
$g=0;
$h=0;
$i=0;
$j=0;
$k=microtime_float();
for ($a=10; $a=1; $a--) {
for ($b=10; $b=1; $b--) {
$g=$a+$b;
for ($c=10; $c=1; $c--) {
$h=$g+$c;
for ($d=10; $d=1; $d--) {
for ($e=10; $e=1; $e--) {
$i=$d+$e;
for ($f=10; $f=1; $f--) {
if ($h==($i+$f)) $j++;
}
}
}
}
}
}
$k=microtime_float()-$k;
echo "Счастливых билетов: $j шт.";
echo "Ьr";
echo "Время расчёта: $k с.";
echo "Ьr";
?




Результат - 1,9 с, загрузка процессора - 50%, быстродействие - 3,7 млн.оп./с, а эффективность - 0,9%! Вот это номер! Меньше процента! Странно, но почему-то php использует только половину процессорного времени... А вот результаты не ахти. С другой стороны, от интерпретатора веб-скриптов вряд ли стоит ожидать многого.


Теперь, ruby 2.1.15:


a=0
b=0
c=0
d=0
e=0
f=0
g=0
h=0
i=0
j=0
k=Time.new
for a in 0..9
for b in 0..9
g=a+b
for c in 0..9
h=g+c
for d in 0..9
for e in 0..9
i=d+e
for f in 0..9
if h==i+f
j=j+1
end
end
end
end
end
end
end
k=Time.new-k
puts j
puts k




Усреднённый результат - 5 с, загрузка процессора - 98%, быстродействие 1,4 млн.оп./с, а эффективность - 0,35%.


Далее, набортный perl 5.20.2:


use Time::HiRes qw(time);
$a=0;
$b=0;
$c=0;
$d=0;
$e=0;
$f=0;
$g=0;
$h=0;
$i=0;
$j=0;
$k=Time::HiRes::time;
for ($a=10; $a=1; $a--) {
for ($b=10; $b=1; $b--) {
$g=$a+$b;
for ($c=10; $c=1; $c--) {
$h=$g+$c;
for ($d=10; $d=1; $d--) {
for ($e=10; $e=1; $e--) {
$i=$d+$e;
for ($f=10; $f=1; $f--) {
if ($h==($i+$f)) {
$j++;
}
}
}
}
}
}
}
$k=Time::HiRes::time-$k;
print($j);
print"\n";
print($k);
print"\n";



Усреднённый результат - 9,4 с, загрузка процессора - 98%, быстродействие 0,74 млн.оп./с, эффективность - 0,18%. Perl использует процессорное время по максимуму, а результат ещё хуже, чем у ruby.


Наконец, для выполнения теста интерпретаторами python 2.7 и python 3.4.2 будет использоваться один и тот же скрипт:


a=0
b=0
c=0
d=0
e=0
f=0
g=0
h=0
i=0
j=0
from datetime import datetime
k=datetime.now()
for a in range(10):
for b in range(10):
g=a+b
for c in range(10):
h=g+c
for d in range(10):
for e in range(10):
i=d+e
for f in range(10):
if h==i+f:
j=j+1
k=datetime.now()-k
print (j)
print (k)


Python 2 почти не отличился от perl:



Усреднённый результат - 9,4с, загрузка процессора - 98%, быстродействие 0,74 млн.оп./с, эффективность - 0,18%.


А что же обновлённый python 3.4.2?




Усреднённый результат - 15,2с, загрузка процессора - 98%, быстродействие 0,46 млн.оп./с, эффективность - 0,114%. Что-то уж совсем python3 не блещет, сделаем ему поблажку, возьмём за результат лучший - 14с, тогда быстродействие выходит ровно 0,5 млн.оп./с, а эффективность интерпретатора чуть лучше - 0,1235%. Это значит, что интерпретатор выполняет программу в 800 раз медленнее, чем может сам микропроцессор!


Мне могут возразить, что тест испытывался на процессоре, разработанным в России, и наверняка в нём присутствуют какие-либо подводные камни, как в первом мультклеточном процессоре? Отнюдь, прогон теста Виталием Самуровым на RaspberryPi Zero с разогнанным до 1ГГц ARM11 процессоре BCM2835 показал сходные результаты:


pi@raspberrypi:~/PythonStuff $ python tickets.py
55252
0:00:05.366339
pi@raspberrypi:~/PythonStuff $ python3 tickets.py
55252
0:00:05.084681


Python 2 показал результат - 5.3 с, быстродействие 1,3 млн.оп./с, эффективность - 0,13%.


Python 3 показал почти такой же результат - 5,1 с, быстродействие 1,4 млн.оп./с, эффективность - 0,14%.


Я был просто изумлён результатами - я никак не ожидал что интерпретатор веб-скриптов - php5-fpm показал наилучшее быстодействие из всех имеющихся у меня интерпретаторов, а Arduino UNO вышла победителем! Raspberry Pi даже при подсчёте "счастливых" на php за 0,9 секунд не дотягивает до Arduino! Только разогнанный до 1ГГц RaspberryPi Zero может при выполнении тестового php-скрипта победить оптимизирующий компилятор 16МГц микроконтроллера Arduino. А что же официально рекомендуемый для Raspberry Pi python 3? Увы, но с ним даже 1ГГц RaspberryPi Zero в шесть раз медленнее старенькой 16МГц Arduino.


Что в итоге? Апгрейд с Arduino до Raspberry Pi в самом лучшем случае не даёт выигрыша, а на деле получается шаг назад. Вроде бы приобретён микрокомпьютер в полста раз мощнее микроконтроллера старенькой отладочной платы, а на деле счастливый обладатель новинки благодаря ленивым программистам интерпретаторов делает шаг назад! Неужто, один маленький шаг назад одного человека выливается в гигантский прыжок назад для всего человечества? Python 3 на глазах превращает вычислительную мощь процессора в вычислительные мощи интерпретатора - российская 90-нм СБИС К1879ХБ1Я, выпускающаяся с 2011 года,




скатывается до советской 6-мкм БИС К580ИК80 образца 1977 года,




это просто огромный, в 34 года, шаг назад! Даже если учесть примерно десятилетнее отставание новейших российских микропроцессоров от мирового уровня - всё равно, неэффективный программный код отбрасывает пользователя устройства на десяток-другой лет назад! А что, если скомпилировать тест с помощью gcc? Увы, компилятор gcc в комплект программного обеспечения Raspberry Pi не входит и его придётся устанавливать отдельно.

Для завершения нашего исследования, скомпилируем силами gcc вот эту программу:


#include
#include
char a;
char b;
char c;
char d;
char e;
char f;
char g;
char h;
char i;
unsigned short j;
float k;
float l;
int main() {
k=clock();
j=0;
for (a=10; a=1; a--) {
for (b=10; b=1; b--) {
g=a+b;
for (c=10; c=1; c--) {
h=g+c;
for (d=10; d=1; d--) {
for (e=10; e=1; e--) {
i=d+e;
for (f=10; f=1; f--) {
if (h==(f+i)) j++;
}
}
}
}
}
}
k=clock()-k;
l=k/CLOCKS_PER_SEC;
printf("%d",j);
printf("\n");
printf("%f",l,"\n");
printf("\n");
return 0;
}


Поскольку, gcc имеет широкие возможности по оптимизации программного кода, будем компилировать и запускать тест на всех уровнях оптимизации — по умолчанию, О0, О1, О2 и О3:




GCC выдал результаты на значительно превосходящие ничтожные потуги рекомендуемых к применению на Raspberry Pi компиляторов! Вдобавок, результаты расчётов оказались весьма стабильны и не имели такого явного разброса, как на ruby, perl и python 2/3.

Итак, результат оптимизации по умолчанию — 0,174 с, быстродействие — 40 млн.оп./с, эффективность — 10%. Что-то не очень, хотя уже в десять раз быстрее php и в 81 раз быстрее python 3.

Результат с выключенной оптимизации О0 тот же — 0,174 с, быстродействие — 40 млн.оп./с, эффективность — 10%. Очевидно, в gcc по умолчанию оптимизация отключена.

Результат с начальной оптимизацией О1 в семь раз лучше — 0,024 с, быстродействие — 290 млн.оп./с, эффективность — 72%. Вот это уже похоже на правду, вот она мощь процессора!

Результат со средней оптимизацией О2 тот же — 0,02 с, быстродействие — 350 млн.оп./с, эффективность — 86%. Можно сказать, что результат приближается к ассемблерному. За 14% проигрыш по производительности программе, написанной в машинных кодах с тщательнейшей оптимизацией программист платит значительно меньшей трудоёмкостью программирования. Теперь над задачей оптимизации работает микропроцессор, а не ломает голову человек.

Наконец, максимальное ускорение! Результат с наибольшей оптимизацией О3 поражает — 0,014 с, быстродействие — 500 млн.оп./с, эффективность — 123%. Чудес не бывает, а вечный двигатель построить ещё никому не удалось. Компилятор для дальнейшей оптимизации просто развернул внутренний цикл в линейный участок, процессор теперь не выполняет почти миллион команд переходов, и в этом кроется секрет повышения производительности, показавшего результат, формально превышающий возможности процессора! Расплата — увеличение размера кода программы почти вдвое. Так, что здесь можно только выбирать из двух зол меньшее — производительность или размер файла.

Да, с помощью gcc можно получить полный доступ к процессорной мощи Raspberry Pi! Но компилятор имеет много параметров, и запуск компиляции может оказаться труднодоступным для начинающего любителя. Выложивший денег за новинку любитель получает в красивой упаковке устройство, с настолько урезанными возможностями, что на деле оказывается хуже имеющегося старого. Да, с помощью новинки можно управлять умным домом даже через полмира - но вряд ли удастся выжать из Raspberry Pi стандартными средствами большего, чем из старой Arduino.

А в чём же заключается шаг назад при приобретении Raspberry Pi вместо Arduino UNO, ведь есть прогрессивный компилятор gcc, позволяющий отбросить ограничения нечистого на руку производителя и получить все вычислительные возможности процессора в своё полное и безраздельное пользование? Есть интересный проект для Arduino UNO от энтузиастов — прошивка GRBL, позволяющая превратить Arduino UNO в настоящий контроллер для станка с числовым программным управлением! Проект GRBL был разработан Сайменом Свале Скогсрудом и увидел свет в 2009 году,через четыре года после появления Arduino. Контроллер GRBL мог только выполнять команды ЧПУ с по СОМ-порту, но мог выполнять линейные и круговые интерполяции, но для управления им достаточного было самого простого компьютера! Да, в промышленном станке контроллер ЧПУ на Arduino мог вызвать только насмешливую улыбку, а вот в самодельном настольном фрезерном с достоинством фрезеровал и сверлил печатные платы, а также, небольшие фигурные детали из алюминия или пластика. Неудивительно, что проект GRBL приобрёл широкую популярность и не теряет актуальности и поныне. А как же Raspberry Pi, увидевший свет в 2012? Через четыре года в 2016, да и поныне нет простой и понятной программы, позволяющей превратить Raspberry Pi в контроллер ЧПУ станка. Есть проект LinuxCNC, который для нормальной работы в многозадачной ОС требует доработанного ядра. Даже система ЧПУ Mach3 вполне нормально работает под управлением Windows NT, благодаря специальному драйверу ядра. Но, не смотря на прошедшие годы после выпуска Raspberry Pi, я так и не нашёл метода установки LinuxCNC без пересборки ядра...

Декабрь 2018

Обновлено - 7.06.2021. Статья была изначально была написана для сайта Довнгрейд Романа Карпача и опубликована там же. Однако, довнгрейд-портал внезапно прекратил своё существование 25 марта 2021 и я перенёс статью на свой сайт.



Сайт работает на микрокомпьютере