CircuitPython Turbo ускорил один модуль RP2040 почти в 20 раз
На Metro RP2040 один и тот же fixed-point Mandelbrot сначала считал поле 160×120 за 8335.3 мс, затем за 4778.0 мс, затем за 422.9 мс. Контрольная сумма результата во всех трёх прогонах осталась равной 407644. Изменился способ исполнения одного вычислительного модуля: байткод CircuitPython сменили предкомпилированные Native и Viper.
Именно так устроен CircuitPython Turbo. Основная программа, включая управляющую логику, работу с дисплеем, состояниями, файлами и периферией, остаётся в привычном code.py. Разработчик выбирает горячую функцию — например, внутренний цикл FIR, распаковку битового потока, CRC, преобразование пикселей или интерполяцию — и собирает её на компьютере. На плату попадает файл .mpy, который импортируется вместо исходного модуля.
Машинный код приезжает на плату готовым
Turbo временно добавляет к выбранной функции декоратор MicroPython Native либо Viper, вызывает mpy-cross с ISA целевого MCU и раскладывает результат в каталог библиотеки. Загрузчик в специальной сборке CircuitPython и небольшой import-shim направляют импорт на нужный .mpy. Компилятора на микроконтроллере нет; заявленный дополнительный объём native-загрузчика составляет 2–3 KB.
Два режима и цена 0.42 секунды
Это важная граница: расширение .mpy само по себе ещё не означает машинный код. Контейнер способен хранить и байткод. Нативный .mpy привязан к архитектуре процессора, версии формата, разрядности small int и параметрам ABI. Файл, собранный для другой ISA, плата отвергнет как несовместимый. Исходный .py в том же каталоге также может получить приоритет при импорте, поэтому раскладка файлов здесь является частью механизма, а не мелочью упаковки.
В Native инструкции функции исполняются нативно, однако аргументы и результаты сохраняют семантику объектов Python. В задокументированном тесте этот шаг сократил время с 8.3353 до 4.7780 с: ускорение составило 1.74 раза. Это полезно для кода, где заметную долю времени съедает интерпретатор, но сами операции всё ещё проходят через Python-объекты.
Ускорять стоит участок, который действительно считает
Viper предлагает гораздо более жёсткую сделку. Программист размечает машинные целые и указатели, ограничивает диапазоны, работает с буферами и может отказаться от удобной произвольной точности Python integer. В Mandelbrot использовалась 12-битная fixed-point арифметика; Viper закончил расчёт за 422.9 мс, дав 19.71 раза относительно байткода. На Cortex-M0+ промежуточные значения обязаны укладываться в 32 бита: переполнение уже не превращается незаметно в большое Python-число и способно изменить результат.
В репозитории Turbo опубликована и более широкая таблица для 12 плат с заявленными 19.5×–71.7× в Viper относительно float-bytecode варианта Mandelbrot. Эти значения нельзя склеивать с измерением Metro RP2040: там другой baseline и, вероятно, иной вариант вычислений. Для конкретно описанного fixed-point теста надёжные ориентиры — 1.74× в Native и 19.71× в Viper.
Такой модуль не сделает быстрее ожидание АЦП, SPI-транзакцию, вывод на дисплей или паузу DMA. Измерение Adafruit охватывает только вычисление Mandelbrot без I/O. Если ускоренный цикл занимал 10% времени прошивки, даже бесконечно быстрый Viper ограничен примерно 1.11-кратным ускорением всей задачи.
Зато Turbo убирает болезненный момент, где прототип на Python обычно приходится переписывать целиком ради одного узкого места. Он оставляет Python там, где код описывает поведение устройства, и позволяет вручную приблизить один хорошо очерченный вычислительный участок к машинному коду. Для ISR, управления DMA и жёстких задержек C или специализированная прошивка по-прежнему нужны. Для конечного автомата с одним прожорливым циклом появляется ещё один инженерный маршрут.