bytes-per-token · сентябрь 2026 · in English

Языковая модель на микроконтроллере за $10 — в 3,8–8,2 раза быстрее открытого аналога

Мы измерили, где на ESP32-S3 теряется время, и переписали выполнение языковой модели под этот чип. На той же плате и тех же весах наш движок выдаёт тот же текст, что и esp32-ai, в несколько раз быстрее. А собственные модели, устроенные под этот чип, пишут лучше их модели при той же скорости.

8,2×быстрее esp32-ai: та же модель, та же плата, тот же текст
3,8×при полностью одинаковом расчёте (без двухступенчатого выбора слова)
0,63 / 0,86бита на байт текста (меньше лучше): наша модель на 8,6 млн параметров против модели esp32-ai на 29 млн; на нашем движке 81 и 94 ток/с в среднем по контексту
0расхождений в битах между платой и ПК во всех тестах
ESP32-S3-N16R8 · 2 ядра Xtensa LX7 240 МГц · 512 КБ SRAM · 8 МБ PSRAM · 16 МБ Flash

Один и тот же текст, одна и та же плата

Скорости реальные: измерены на плате, промпт «Once upon a time», 200 токенов

esp32-ai (их прошивка)

10,6 ток/с
Once upon a time
0 / 200 токенов0,0 с

Наш движок, та же модель

86,8 ток/с
Once upon a time
0 / 200 токенов0,0 с

Тексты совпадают до символа: 759 из 759. Разница только во времени: 18,8 с против 2,3 с.

Главное наблюдение

Скорость упирается в память, а не в вычисления

Чтобы написать одно слово, модель должна прочитать все свои веса. У чипа три вида памяти с разницей в скорости почти в 10 раз, а векторный блок PIE считает в 21 раз быстрее обычного кода. После его включения процессор в основном ждёт данные.

Скорость чтения памяти

МБ/с, измерено на нашей плате (последовательное чтение, 1 ядро)

Такты на одно умножение-сложение (int4)

Меньше лучше. Веса в SRAM, ядро на C против ядра на ассемблере PIE

Отсюда правило, которое определило всю работу: ускорение = меньше байт на каждое слово. Байты на слово = бит на вес × доля читаемых весов ÷ слов за одно чтение. Каждый приём ниже уменьшает один из этих множителей.

Путь

Что мы сделали, по порядку

  1. Этап 0

    Калькулятор скорости чипа

    Модель затрат по операциям: чтение каждой памяти плюс вычисления. На чужих замерах, которых модель не видела при подгонке, ошибка 3–5% (всего две проверочные точки); на нашем движке она занижает время на 10–14%. Он позволил отбрасывать идеи на бумаге до написания кода.

  2. Этап 1

    Замеры платы

    Собственная прошивка-бенчмарк: скорость SRAM, PSRAM и Flash, ядра PIE, DMA. Выводы: PIE в 21 раз быстрее простого C (в ~13 раз быстрее ядра esp32-ai); после него узкое место — PSRAM (83 МБ/с на двух ядрах); шина PSRAM даёт ~90 МБ/с на всех, поэтому DMA не добавляет пропускной способности.

  3. Этап 2

    Эксперименты с устройством модели

    27 моделей на ноутбуке, каждый сравниваемый вариант в двух запусках (кроме одного повторного 2-битного). Справочник PLE делает модель умнее при том же объёме чтения; ReLU позволяет пропускать 79% нейронов без потерь; обучение под 4 бита почти бесплатно; 4–6 слоёв лучше 12.

  4. Этап 3

    Движок на плате

    C-код, общий для ПК и платы, плюс ассемблерные ядра PIE. Три версии: 70 → 98 → 110 токенов/с на нашей модели. Каждое изменение измерено отдельно; неудачные откатаны и записаны.

  5. Сравнение

    Модель esp32-ai на нашем движке

    Их опубликованные веса без переквантования. Их прошивка и наш движок на одной плате, на их же тесте: 94,1 против 11,5 мс на токен, текст совпадает полностью.

  6. Этап 4

    Несколько слов за одно чтение

    Угадывание следующих слов на этом чипе не окупается (отрицательный результат). Пакетная обработка заранее известных слов (промпт, однозначные куски JSON) даёт ×1,5–2,8.

  7. Этап 5

    Своя модель по рецепту

    Модель затрат, откалиброванная на плате, предсказала скорость вариантов с точностью 1–5%. Лучший вариант (словарь 8192, справочник вдвое шире, 8,6 млн параметров) после обучения на 70 млн токенов даёт 0,63 бита на байт против 0,86 у модели esp32-ai на 29 млн; наша модель на 3,4 млн лучше её при почти той же скорости.

Модели

Как сделать модель умнее, не читая больше

Качество при одинаковом объёме чтения на слово (~900 КБ)

Ошибка на отложенных текстах (val loss), меньше лучше. Две точки на модель — два независимых запуска.
обычная и урезаннаяс экспертами (MoE)со справочником (PLE)
ПриёмКачество (val loss)Скорость (плата или модель затрат)Итог
Справочник PLE: таблица на слово во Flash, читается одна строка−0,022…−0,039+0,5 мс, платаработает
ReLU вместо SwiGLU и пропуск молчащих нейронов (79%)−0,002×1,3, платаработает
Обучение сразу под 4 бита (QAT)+0,024~7× меньше байт, чем fp32 (с масштабами)работает
Эксперты MoE (4 эксперта, читается 1)−0,009≈, модель затратслабо
2 бита при том же размере (при вдвое большем числе весов и том же объёме чтения: +0,13)+0,305×1,5, модель затрат (на плате не запускали)дорого по качеству
12 слоёв вместо 6+0,069−15%, модель затратхуже по обоим

Итоговая модель combo = PLE + ReLU + 4 бита: val loss 2,010 против 2,024 у обычной 4-битной (среднее двух запусков, лучше в обоих), и на 22% быстрее её на плате (98 против 80 ток/с, движок v2).

Рецепт против чужой модели: одинаковый текст, одна плата

Модели с разными словарями сравниваются в битах на байт текста: сколько информации модели не хватает, чтобы предсказать каждый байт. Первые 5000 отложенных историй, каждая модель со своим токенизатором и со своими 4-битными весами, как их выполняет движок. Скорость — среднее по всему контексту 256 на нашем движке (двухступенчатый выбор слова, PSRAM 80 МГц).

МодельПараметровБит на байтток/с (среднее, плата)
esp32-ai TinyStories (их открытая модель)28,9 млн0,863~94
combo (наша: PLE + ReLU + 4 бита)3,4 млн0,732~91
справочник PLE вдвое шире4,9 млн0,716~84 (прошлая версия движка)
словарь 8192 + справочник вдвое шире8,6 млн0,704~81
то же, обучение на 70 млн токенов вместо 208,6 млн0,625~81
словарь 16384 + справочник вдвое шире16,0 млн0,706— (прогноз ~74)

Проверка честности: наша оценка их модели (2,45 нат на токен) совпадает с тем, что авторы сами публикуют (перплексия 11,4 до квантования, «~+1» после). У их модели почти все параметры — словарь на 32 тыс. токенов и справочник, а ядро всего 0,56 млн; на детских историях такой словарь почти не используется, а наш вариант со словарём 16384 показал, что справочник насыщается. Их модель мы запускаем только на нашем движке: их прошивка выполняет её со скоростью 10,6 ток/с и наши модели запустить не может.

Движок

Где время уходит у них и у нас

Разбивка времени на один токен, одна и та же модель esp32-ai

мс на токен по стадиям, их тест (200 токенов). Профиль esp32-ai — их собственный вывод на нашей плате.
4 бита + PIE

Веса хранятся в 4 битах и распаковываются векторными командами. У esp32-ai 8 бит и обычный C: вдвое больше байт и в ~13 раз больше тактов на операцию (6,2 против 0,47).

15% строк

Двухступенчатый выбор слова: 256 групп слов, сначала оценка групп, потом полный расчёт только лучших. Совпадает с полным выбором в 99,4–99,8% позиций; прочитанные строки побитово равны полному расчёту.

2 ядра

Каждое умножение делится пополам между ядрами, второе ядро ждёт работу в активном цикле: передача задачи стоит 83–93 такта.

SRAM

Сначала буферы движка, затем самые читаемые веса в быструю внутреннюю память (~210 КБ), остальное в PSRAM, таблица PLE остаётся во Flash.

48% FFN

ReLU «сначала ворота»: считаем ворота, а две другие матрицы читаем только для живых нейронов. Новое ядро на аккумуляторе QACC для транспонированной матрицы.

−1 мс

Работа вне умножений: квантование, GELU по таблице, выбор групп. По профилю, а не наугад, сжали её с 2,2 до 1,2 мс на токен.

Рост скорости движка на нашей модели combo

Токенов в секунду, 2 ядра, генерация после короткого промпта
Честное сравнение

Одна плата, одна модель, один тест

Прошивку esp32-ai мы собрали их способом (arduino-cli, -O3, их раздел Flash) и измерили на нашей плате. Отпечаток модели a9bdd778 совпал с их метаданными, три прогона дали одно и то же значение. Их собственная опубликованная цифра — 94,9 мс, наш замер — 94,1 мс.

мс на токен, модель esp32-ai (29 млн параметров), позиции 4–203

Меньше лучше
ДвижокВыбор словаPSRAMмс/токенток/сУскорение
esp32-aiполный, 8 бит80 МГц94,1010,61×
нашполный, 4 бита80 МГц25,0040,03,8×
нашдвухступенчатый80 МГц11,5286,88,2×
нашполный120 МГц*18,9052,95,0×
нашдвухступенчатый120 МГц*9,17109,010,3×

* PSRAM на 120 МГц в ESP-IDF помечена как экспериментальная: «работает при стабильной температуре». Замер при комнатной температуре, сбоев не было.

Что не сработало

Отрицательные результаты тоже измерены

ИдеяОжиданиеЗамерПочему
Угадывать 2–4 следующих слова и проверять за одно чтение×1,3 по калькулятору×0,8–1,05после оптимизаций треть времени — вычисления на каждое слово; угадчик даёт лишь 1,4–1,6 слова за проход
DMA подгружает веса, пока ядро считает−30%не быстреешина PSRAM даёт ~90 МБ/с на всех; DMA (77 МБ/с) только делит её с ядрами, а два ядра сами читают 83
Масштабы весов в 16 битах вместо 32−6% байт+5% времениперевод 8192 чисел за слово дороже сэкономленного чтения
Держать последние значения внимания в SRAM−1 мс−0,06 мс вниманиетормозили не записи, а задержки чтения PSRAM (всего вышло −0,39 мс, остаток не объяснён)
Веса из генератора «зёрен» (SeedLM), сжатие весоввыигрыш на GPUпроигрыш на бумагена чипе нет лишних тактов: распаковка дороже чтения
Новизна

Чем проект отличается от существующих

ПроектГде работает модельСкорость на ESP32-S3Что естьЧего нет
Этот проектESP32-S3, полностью автономно87–110 ток/с (29 млн), 110–134 (2 млн); в конце контекста ~72PIE-ядра, 2 ядра, двухступенчатый выбор слова, разреженный FFN, пакеты, модель затрат, побитовая проверкаприкладная модель (пока детские рассказы)
slvDev/esp32-aiESP32-S310,6 ток/с (измерено нами)модели TinyStories и Barista, PLEPIE, 4-битное выполнение, пропуск работы
esp32-tinyLLM (Rust)ESP32-S3~10,5 ток/с (их данные)методика бит-в-бит провероквекторный блок, большие выигрыши по байтам
Порты Needle (вызов команд)ESP32-S3~1,5 ток/с (их данные)вызов инструментов по схемескорость
Deeploy (компилятор)RISC-V MCU с NPU—тайлинг, DMA, малые LLMESP32 / Xtensa PIE

1. Самый быстрый известный нам запуск LLM на ESP32-S3

На тех же весах и с тем же текстом: 3,8× при одинаковом расчёте и 8,2× с двухступенчатым выбором слова. Других открытых движков с PIE для LLM мы не нашли.

2. Модели, устроенные под чип

Сравнение устройств моделей при одинаковом объёме чтения на токен и проверка на плате. Наша модель на 3,4 млн параметров пишет лучше открытой модели esp32-ai на 29 млн при почти той же скорости на нашем движке (0,73 против 0,86 бита на байт), а лучшая – 0,63.

3. Приёмы серверного мира, проверенные на MCU

Двухступенчатый выбор слова, пропуск неактивных нейронов и пакетная обработка известных токенов на микроконтроллере в найденных источниках не встречались. Мы показали, какие из них работают, а какие вредят, и почему.

4. Методика, которой можно доверять

Модель затрат с проверкой на чужих данных, побитовое совпадение ПК и платы, проверка каждого ядра против эталона на C, повтор каждого эксперимента.

«Не нашли» означает поиск по GitHub, Hugging Face, arXiv и патентам; это не доказательство отсутствия.

Проверка

Почему цифрам можно верить

бит в бит

ПК, плата с PIE на двух ядрах, на одном ядре и на обычном C дают одинаковые отпечатки всех токенов и всех логитов в каждом прогоне.

±0,003

Ошибка модели на отложенном тексте в движке отличается от PyTorch не больше чем на 0,003. Выбор слова совпадает в 98–99% позиций; расхождения только на почти равных вариантах.

<0,1%

Разброс между повторами одного и того же прогона. Это не погрешность по разным текстам: от начала к концу контекста скорость падает в 1,6 раза. Все замеры сохранены в логах в репозитории.

Живое демо

Попробуйте сами

Плата с моделью esp32-ai и нашим движком подключается к любому компьютеру по USB. Страница в Chrome или Edge подключается к плате напрямую, без установки программ. Вы пишете начало истории, плата дописывает её в реальном времени, на экране видна скорость каждого токена.

  1. Подключите плату кабелем к разъёму COM.
  2. Откройте engine/demo/index.html в Chrome или Edge.
  3. «Подключить плату» → выберите порт CH343.
  4. Введите начало истории по-английски → «Написать».

Все модели учились на детских рассказах. Для историй лучше всего наша модель со словарём 8192 (varC); модель esp32-ai тоже поддерживается; дообученная combo_qa ещё отвечает на простые вопросы по сказке (режим «Вопрос по сказке», docs/qa-finetune.md). Модель меняется прошивкой одного файла: engine/demo/flash_demo.sh.

ПК  → G 120 700 40 <seed> 0 7 433 447 259 405 …
плата → P 29.6
плата → T 11 10320
плата → T 612 10288
…
плата → E 120 10.32

Ограничения

Модели маленькие (3–29 млн параметров) и обучены на детских рассказах, 20 млн токенов на модель, один LR для всех; сравнение качества с esp32-ai — на тексте нашего распределения (их оценка при этом совпала с их собственной); скорость зависит от длины контекста («110 ток/с» — позиции 13–60; сравнение с esp32-ai — на их тесте, позиции 4–203); режим PSRAM 120 МГц экспериментальный; энергия на токен не измерялась, второе ядро занято активным ожиданием; версия ядра Arduino при замере esp32-ai — 3.3.3 (у авторов 3.3.10).