Немного о скорости выполнения программ на Arduino-совместимых платах. Часть 2: Пишем программу оптимально: пора сниматься с ручника
- 10.07.2017
- Автор: admin
- Рубрика: Полезные советы

В прошлой части было рассказано об основных факторах, влияющих на скорость выполнения программ, а также были описаны методы её повышения повышением вычислительной мощности платы.
Скорость выполнения программы сильно зависит от вычислительной мощности процессора. Но больше всего на скорость выполнения программы влияет её структура. И для ускорения работы программы её нужно оптимизировать, сокращая число операций, выполняемых в критичных к скорости местах программы.
Рассмотрим это на примере одних из самых часто выполняемых операций – на примере цифрового вывода в линию порта функцией digitalWrite. Вы когда-либо задавали себе вопрос «Сколько времени занимает запись единицы или нуля в линию порта?». Вряд ли. На это редко обращают внимание. А зря…
Давайте измерим время выполнения функции digitalWrite. Для этого возьмём стандартный скетч «Blink» и просто уберём из него задержки (рис. 1) – будем как можно быстрее записывать в линию порта ноль и единицу. Загрузим получившийся скетч в плату и логическим анализатором измерим продолжительность единицы и нуля на линии порта (рис. 2).
Результаты удивляют — примерно по 7.5 мкс (микросекунд) на запись единицы и нуля. Удивляют тем, что такая простая функция может съедать так много времени.
Одна операция выполняется микроконтроллером семейства AVR за один такт. При стандартной тактовой частоте 16 МГц на выполнение одной элементарной операции уходит 0,0625 мкс. Следовательно, функция digitalWrite состоит из примерно 120 элементарных операций. Много это или мало? Это ужасно много. Забегая наперёд, скажем, что для этого достаточно всего двух элементарных операций. Так почему же функция digitalWrite содержит аж 120?
Всё кроется в самой концепции Arduino. Платформа Arduino предназначена для новичков, поэтому в ней реализовано максимальное удобство для пользователя, местами в ущерб другим характеристикам. Например, пользователь при использовании функции digitalWrite оперирует лишь условным номером линии порта (№15 в нашем примере). Ему, в частности, не нужно знать внутреннюю структуру аппаратных портов микроконтроллера, допустимые режимы работы каждой линии порта, их привязку к выводам самого микроконтроллера. Этим всем, как раз, и занимается функция digitalWrite. Она ищет соответствующие условному номеру регистры аппаратного порта, проверяет, в каком состоянии нужная линия порта, в каком режиме она работает (не используется ли эта линия порта при работе цифровых интерфейсов, ШИМ и т. д.). К тому же, эта функция написана не оптимально.
Забота о пользователях – это, несомненно, хорошо. Но что, если вы уже стали опытным ардуинщиком и в вашем новом устройстве важна скорость работы программы? Решение есть. Реализуем цифровой вывод в линию порта стандартным способом языка С – просто запишем значение «1» и «0» в нужный разряд нужного регистра микроконтроллера по схеме платы. Для этого найдём соответствующие наименование регистра и номер разряда. В нашей плате условной линии №15 соответствует линия PB7 — разряд 7 порта В, значит нам нужны регистр PORTB, 7-й разряд. Исправляем скетч, как показано на рис. 3, загружаем скетч в плату и повторяем измерения.
Смотрим на результаты (рис. 4). Ну это же совсем другое дело! Всё, как мы говорили (ну почти): 0,125 мкс (2 операции) на запись нуля и 1 мкс (16 операций) на запись единицы (из них 2 – собственно запись и ещё 14 безжалостно съедаются функцией loop, которая тоже не оптимизирована). То есть, на один цикл работы у нас теперь уходит 1.125 мкс (18 тактов) вместо 15 мкс (240 тактов) – мы ускорили работу программы более чем в 13 раз. Но это – не предел оптимизации. Продолжение — в следующей части.
Выводы:
- Структура программы сильно влияет на скорость её выполнения, поэтому критичные к скорости участки программы обязательно нужно оптимизировать.
- Стандартные функции Arduino написаны неоптимально, но зато имеют защиту от неопытности пользователей.