В прошлой статье мы честно сравнили MicroPython и C на Raspberry Pi Pico и пришли к выводу, что спорить бессмысленно — каждый инструмент хорош в своей задаче.
Теперь логичный вопрос: а можно ли выжать из MicroPython максимум и приблизиться к скорости C?
Можно. И иногда — действительно в 5–10 раз.
Речь пойдёт о платах на базе RP2040 и RP2350, используемых в Raspberry Pi Pico и Pico 2.
1. Главное правило: убрать Python из «горячего» цикла
Самая большая потеря производительности происходит, когда:
- Python выполняет tight loop
- внутри есть обращения к объектам
- создаются новые переменные
- используются списки или строки
Пример медленного кода:
while True:
pin.toggle()
Это Python-объект, метод, диспетчеризация, проверка типов.
Ускорение №1: сохранить ссылки локально
toggle = pin.togglewhile True:
toggle()
Почему быстрее?
- Убирается поиск атрибута
- Уменьшается нагрузка на интерпретатор
Прирост: до 30–40%
2. Использовать @micropython.native и @micropython.viper
Это самый мощный встроенный способ ускорения.
native
@micropython.native
def fast_loop():
for i in range(1000):
pass
Компилируется в нативный код ARM.
Ускорение: 2–5×
viper — почти C внутри Python
@micropython.viper
def fast_add(a: int, b: int) -> int:
return a + b
Особенности:
- статическая типизация
- прямые операции с int
- указатели
- почти машинный код
Ускорение: 5–20×
Ограничения:
- меньше динамики
- осторожность с типами
3. Использовать array вместо list
list — это:
- динамический объект
- хранит ссылки
- дорого по памяти
array:
import arraybuf = array.array("H", [0]*1024)
Плюсы:
- компактно
- быстрее доступ
- меньше GC
Прирост: 2–3× при работе с данными
4. Предварительное выделение памяти
Плохой стиль:
data = []
while True:
data.append(adc.read_u16())
Это:
- realloc
- GC
- фрагментация
Лучше:
buf = array.array("H", 1024*[0])
for i in range(1024):
buf[i] = adc.read_u16()
Прирост: в разы при потоковой обработке
5. Отключать GC в критических местах
Garbage Collector может внезапно остановить выполнение.
import gcgc.disable()
critical_code()
gc.enable()
Важно:
- только если уверены, что нет утечек
- иначе переполнение памяти
Это не ускоряет вычисления напрямую, но убирает паузы.
6. Использовать PIO — убрать timing из Python
Огромный плюс RP2040 — наличие PIO.
Python медленный для точного timing.
Но если:
- генерация сигнала
- протокол
- захват импульсов
перенести в PIO — скорость становится аппаратной.
MicroPython лишь:
- загружает программу
- читает FIFO
Эффект: ускорение в сотни раз для timing-задач
7. Использовать DMA вместо циклов чтения
Плохой вариант:
for i in range(1000):
buf[i] = adc.read_u16()
Хороший вариант:
- ADC работает в free-run
- DMA складывает в буфер
- Python только обрабатывает готовые данные
Результат:
- CPU почти не занят
- скорость до 500 kS/s
- Python не тормозит сбор
Это уже архитектурная оптимизация.
8. Избегать float, использовать int
float в MicroPython дорогой.
Плохо:
v = adc.read_u16() * 3.3 / 65535
Лучше:
v = adc.read_u16() * 3300 // 65535
Разница: до 3×
9. Минимизировать создание объектов
Очень медленно:
- строки
- форматирование
- f-строки
- конкатенация
Плохо:
print(f"value={val}")
Лучше:
print("value=", val)
10. Перенести тяжёлый код в C-модуль
Это самый радикальный способ.
Можно:
- написать C-модуль
- подключить к MicroPython
- вызывать как обычную функцию
Архитектура:
MicroPython:
логика
интерфейсC:
DSP
high-speed обработка
Такой гибрид даёт ускорение в десятки раз, сохраняя удобство Python.
Реальный пример ускорения
Задача: обработка 1024 ADC значений.
Наивный Python:
~12 ms
Оптимизированный:
- array
- локальные ссылки
- int-математика
- @native
~2 ms
Разница: 6×
Если добавить DMA и PIO — разница может стать 10× и больше.
Когда ускорение действительно нужно?
Важно понимать:
MicroPython «медленный» только если:
- частоты > 10–50 кГц
- tight loops
- real-time требования
- большие массивы
Для:
- IoT
- датчиков 1–100 Гц
- логики
- интерфейсов
оптимизация может быть вообще не нужна.
Главный вывод
MicroPython на Raspberry Pi Pico — это не игрушка.
Если:
- убрать Python из горячих циклов
- использовать native/viper
- применять array
- задействовать PIO и DMA
- избегать float
можно получить ускорение в 5–10 раз без перехода на C.
А если добавить C-модуль — производительность становится почти нативной.