Для электронщиков и радиолюбителей

Как ускорить MicroPython на Raspberry Pi Pico в 10 раз

Как ускорить MicroPython на Raspberry Pi Pico в 10 раз

В прошлой статье мы честно сравнили 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

Разница:

Если добавить 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-модуль — производительность становится почти нативной.