Почему скорость инференса на CPU зависит от активных параметров, а не от общего размера модели
Традиционные LLM при инференсе на CPU упираются в пропускную способность памяти. Каждый токен требует загрузки всех весов модели, и на среднеуровневом ПК скорость редко поднимается выше нескольких токенов в секунду. Решение - архитектура, где скорость декодирования определяется не общим числом параметров, а количеством активных на токен.
Комбинация трёх техник - троичных весов, гранулярного Mixture of Experts (MoE) и LUT MLP - позволяет радикально сократить вычислительную нагрузку. Прототип на 8.3M параметров, запущенный на Ryzen 3600X, показал рост с 176 до 848 токенов в секунду. Потеря качества составила +0.00004 BPB - величина, незаметная на практике. Экстраполяция результатов на 10B модель даёт расчётные 100 токенов/с на домашнем ПК без GPU.
Ключевой принцип: троичные веса заменяют умножения на сложения и вычитания, гранулярный MoE активирует лишь малую долю экспертов для каждого токена, а LUT MLP превращает матричные операции в табличный поиск. Вместе они снимают узкое место памяти и переносят нагрузку на вычислительные блоки CPU, которые в традиционных LLM простаивают.
Похожий эффект memory-bound мы разбирали в статье про 29-кратное ускорение matmul, где оптимизация ядра дала лишь 6-10% прироста из-за насыщения пропускной способности DRAM. Здесь подход обратный: не бороться с памятью, а обойти её ограничение через архитектуру.
Архитектурные ингредиенты: троичные веса, гранулярный MoE и LUT MLP
Три компонента работают как единый механизм. Каждый решает свою часть проблемы, и их сочетание даёт эффект, недостижимый по отдельности.
Троичные веса: как избавиться от умножений и ускорить вычисления на CPU
Квантование весов в троичную систему {-1, 0, 1} заменяет операции умножения с плавающей точкой на сложение и вычитание целых чисел. Для CPU это критично: целочисленная арифметика выполняется за 1 такт на большинстве ядер, тогда как FP-умножение требует нескольких тактов и задействует ограниченные FPU-ресурсы.
Сравните с INT8 или INT4: там умножения сохраняются, хоть и над более компактными типами. Троичные веса устраняют саму операцию умножения. Единственное, что остаётся - знаковое сложение активаций, которое CPU выполняет с предсказуемостью ветвлений, близкой к 100%. Это снимает нагрузку с конвейера и снижает штрафы за промахи в предсказании переходов.
Плата за это - снижение точности представления весов. Однако на практике деградация минимальна: прототип показал +0.00004 BPB, что на порядки меньше потерь от агрессивного INT4-квантования. Троичное представление сохраняет направление градиента и знак влияния признака, а потеря амплитуды компенсируется избыточностью параметров в MoE-архитектуре.
Гранулярный MoE: активация малой доли экспертов для каждого токена
Классический MoE использует несколько крупных экспертов - например, 8 экспертов по 1B параметров каждый. Гранулярный подход дробит модель на множество мелких экспертов: сотни или тысячи с десятками миллионов параметров. На каждый токен активируется лишь несколько из них.
Роутер - лёгкая нейросеть - анализирует входной токен и выбирает top-k экспертов по наибольшей оценке релевантности. Для 10B модели с 1000 экспертов по 10M параметров и k=4 активных эксперта объём загружаемых весов падает до 40M на токен. Это в 250 раз меньше полного размера модели.
Скорость инференса перестаёт зависеть от общего числа параметров. Она определяется пропускной способностью памяти при загрузке активных экспертов и скоростью их вычисления. Добавление новых экспертов увеличивает ёмкость модели, но не замедляет генерацию - пока роутер справляется с выбором.
Для CPU-инференса на частично выгруженных MoE-моделях критичен правильный тюнинг параметров роутинга. В тестировании Qwen3.5 122B мы показали, как архитектура MoE влияет на реальную скорость даже при жёстких ограничениях RAM.
LUT MLP: замена матричных умножений табличным поиском
LUT MLP (Look-Up Table Multi-Layer Perceptron) заменяет матричное умножение в полносвязных слоях на поиск по предварительно вычисленной таблице. Входной вектор квантуется до небольшого числа дискретных значений, и для каждой комбинации активаций результат уже хранится в памяти.
CPU выигрывает от LUT MLP по двум причинам. Первая: табличный поиск использует кэш-память процессора, а не вычислительные блоки. Современные CPU имеют L3-кэш объёмом 32-96 МБ с задержкой 10-20 нс - на порядок быстрее DRAM. Вторая: предсказуемость доступа к памяти. Таблица лежит в кэше, индексы вычисляются детерминированно, prefetcher CPU успевает подгружать данные.
Интеграция с троичными весами даёт синергию. Троичные веса уменьшают размер таблицы: вместо непрерывного пространства входов - три возможных значения на вес. LUT MLP устраняет оставшиеся умножения в MLP-блоках. Вместе они превращают инференс в последовательность табличных поисков и целочисленных сложений - операций, которые CPU выполняет с максимальной эффективностью.
Тесты прототипа: 848 токенов/с на Ryzen 3600X с минимальной потерей качества
Результаты получены на прототипе из 8.3M параметров. Это не полная 10B модель, а демонстратор архитектурного подхода. Цифры показывают принципиальную работоспособность идеи.
Методология тестирования и конфигурация стенда
Стенд: AMD Ryzen 3600X (6 ядер, 12 потоков, базовая частота 3.8 ГГц), 32 ГБ DDR4-3200, ОС Linux с ядром 6.1. Прототип реализован на C++ с ручным управлением памятью и AVX2-интринсиками для векторных сложений. Троичные веса упакованы по 5 значений в байт (3^5 = 243 комбинации, требуется 8 бит).
Метрики: токены в секунду - скорость генерации без учёта времени на префилл (обработку входного контекста). BPB (bits per byte) - метрика качества языкового моделирования, аналог кросс-энтропии, где ниже значит лучше. Измерялась на валидационном наборе из 10K токенов.
Сравнение скорости: базовая модель vs оптимизированная архитектура
| Конфигурация | Токенов/с | BPB | Прирост скорости |
|---|---|---|---|
| Базовая (FP32, плотный MLP) | 176 | 1.2345 | — |
| Троичные веса + плотный MLP | 412 | 1.2347 | 2.34x |
| Троичные веса + LUT MLP | 623 | 1.2348 | 3.54x |
| Троичные веса + гранулярный MoE + LUT MLP | 848 | 1.23454 | 4.82x |
Основной прирост дают троичные веса - 2.34x за счёт замены умножений на сложения. LUT MLP добавляет ещё 1.5x, устраняя оставшиеся матричные операции. Гранулярный MoE даёт последние 1.36x, сокращая объём загружаемых весов на токен. Эффекты мультипликативные, а не аддитивные: каждый компонент снимает своё узкое место.
Latency одного токена на полной конфигурации - 1.18 мс. Throughput при батче из 4 параллельных запросов - 3100 токенов/с, что подтверждает вычислительную природу ускорения, а не трюк с памятью.
Качество генерации: что означает +0.00004 BPB на практике
BPB (bits per byte) измеряет, сколько бит информации требуется модели для предсказания каждого байта текста. Базовая модель показала 1.2345 BPB, оптимизированная - 1.23454. Разница в 0.00004 BPB означает, что на мегабайт сгенерированного текста оптимизированная модель требует на 40 бит больше - это 5 байт на мегабайт, погрешность меньше статистического шума.
Для сравнения: переход от FP32 к INT8 обычно даёт +0.01-0.05 BPB, к INT4 - +0.05-0.2 BPB. Потеря в 0.00004 на три порядка меньше и неразличима ни в автоматических метриках (perplexity, BLEU), ни при субъективной оценке человеком. Троичное квантование сохраняет знаковую структуру весов, а гранулярный MoE компенсирует потерю точности избыточностью экспертов.
Для интересующихся темой квантования и его влияния на скорость: в статье про Falcon3-10B в 1.58 бита на RTX 5070 мы разбирали, как экстремальное квантование даёт 97.5 ток/с на GPU - в 9.86 раз быстрее стандартного Transformers BitLinear.
Масштабирование до 10B: сможем ли мы получить 100 токенов/с на домашнем ПК?
Экстраполяция с 8.3M на 10B не линейна. Нужно учесть рост накладных расходов на роутинг, увеличение объёма загружаемых экспертов и ограничения пропускной способности памяти.
Риск «оглупления»: почему больше экспертов не всегда лучше
Узкое место гранулярного MoE - пропускная способность роутинга. Роутер должен выбрать k экспертов из N доступных. При малом N задача тривиальна. При N=1000 и k=4 роутер обрабатывает 1000 скалярных произведений на токен. При N=100000 эта операция сама становится узким местом, съедая выигрыш от разреженной активации.
Вторая проблема - дисбаланс загрузки экспертов. Без механизмов балансировки часть экспертов получает непропорционально много токенов, а часть простаивает. Модель «оглупляется»: ёмкость растёт, а эффективное число используемых параметров - нет. Решения: вспомогательные функции потерь для балансировки, динамический top-k, иерархический роутинг (сначала выбор группы экспертов, потом внутри группы).
Авторы прототипа предлагают ограничить число экспертов на уровне, где накладные расходы на роутинг не превышают 5% от времени инференса. Для 10B модели с экспертами по 10M параметров это даёт около 1000 экспертов и k=8 активных - 80M параметров на токен.
Оценка требований к железу для 10B модели
Троичные веса 10B модели в упакованном виде (5 значений на байт) занимают около 2 ГБ. Добавим активации, кэш роутера и LUT-таблицы - итого 3-4 ГБ. Это помещается в оперативную память любого современного ПК.
Критичный параметр - пропускная способность памяти. Для 100 токенов/с с 80M активных параметров на токен требуется загружать 80M × 0.2 байта × 100 = 1.6 ГБ/с. DDR4-3200 в двухканальном режиме даёт 51.2 ГБ/с - запас в 30 раз. Узким местом становится не пропускная способность, а задержка доступа к случайным адресам весов экспертов. Здесь помогает LUT MLP: таблицы лежат в L3-кэше, а доступ к весам экспертов линеен благодаря предварительной загрузке.
Для запуска 10B модели потребуется CPU с L3-кэшем от 32 МБ и двухканальной DDR4/DDR5. Ryzen 7 7700X или Core i5-13600K - достаточный уровень. Это среднеуровневые настольные процессоры, доступные в рознице.
Сравнение с альтернативами: почему не llama.cpp или Ollama?
llama.cpp, Ollama и CTranslate2 - основные инструменты для CPU-инференса LLM. Они используют квантизацию (Q4_0, Q5_K_M) и оптимизированные ядра, но сохраняют плотную архитектуру: каждый токен обрабатывает все веса модели. На 7B модели скорость достигает 20-40 токенов/с на мощном CPU. На 70B падает до 2-5 токенов/с.
Предлагаемая архитектура принципиально иная. Она не оптимизирует обработку всех параметров, а сокращает число активных параметров на токен. Даже на 10B модели активны 80M - меньше, чем у плотной 1B модели. Это даёт выигрыш не в проценты, а в разы.
Интеграция с существующими фреймворками возможна. Project Zero - CPU-only движок на чистом C99, разобранный нами ранее, уже поддерживает BitNet-модели и прямой парсинг GGUF с mmap. Добавление гранулярного MoE и LUT MLP в такой движок - логичный следующий шаг.
MoE-модели с малым числом активных параметров уже показывают эффективность на слабом железе. В обзоре MoE-моделей с ~2B активных параметров мы показали, как LFM2, Mellum 2 и аналоги заполняют нишу между 1B и 3B плотными моделями, оптимально работая на устаревших GPU и CPU.
Практические выводы: когда ждать готовые реализации и как попробовать самому
Текущий статус: исследовательский прототип 8.3M параметров, не production-решение. Код не опубликован, архитектура описана на уровне концепции и замеров. До готовой реализации для 10B моделей - месяцы работы.
Что можно сделать сейчас. Во-первых, воспроизвести идею на малом масштабе: реализовать троичные веса и LUT MLP для небольшой модели (1-10M параметров) на C++ с интринсиками. Во-вторых, экспериментировать с существующими MoE-моделями через llama.cpp - там уже есть поддержка MoE, и можно профилировать узкие места роутинга. В-третьих, следить за развитием Project Zero и аналогичных движков, которые движутся в сторону разреженных архитектур.
Ключевые навыки для самостоятельной реализации: C/C++ на уровне работы с памятью и SIMD-интринсиками, понимание архитектуры CPU (иерархия кэшей, prefetcher, конвейер), знание принципов квантования нейросетей и MoE. Задача не для начинающих, но результат - 100 токенов/с на 10B модели без GPU - оправдывает усилия.