Discovery
Попробуйте другие наши приложения
Спекулятивное декодирование в vLLM и SGLang: MTP, EAGLE-3 и n-gram в 2026
5 мая 2026 г.104 просмотра1

Больше интересного про инференс, AI-инфраструктуру и практику с LLM я публикую в Telegram-канале @fuckup_files.

TL;DR

Если у вас стоит vLLM или SGLang и вы упёрлись в задержки на decode-фазе — спекулятивное декодирование (speculative decoding) в 2026 уже не выглядит лабораторной игрушкой. Это нормальный инструмент для memory-bound decode, особенно при низкой и средней параллельности. Сейчас в проде чаще всего встречаются четыре подхода: n-gram/suffix (без обучения, чистая алгоритмика), EAGLE-3 (лёгкая голова, смотрит в активации target-модели, ~2–3× ускорения), native MTP (Multi-Token Prediction — если модель действительно обучена с MTP-головами) и DFLASH (block-diffusion драфтер от Z Lab, который на ряде бенчмарков обходит EAGLE-3). В начале мая Google отдельно выпустил Gemma 4 assistant-драфтеры для speculative decoding — это важно, но их лучше не путать с native MTP-головами внутри target-модели. Ниже разбираю, какой метод когда брать, как это завести в обоих движках и почему рост батча убивает спекуляцию быстрее, чем кажется.

Зачем вообще ускорять decode

Если коротко: инференс LLM делится на две фазы с радикально разным профилем нагрузки на железо.

Prefill — это когда мы прогоняем входной промпт через модель и заполняем KV-кеш. Эта фаза обычно compute-bound: тензорные ядра GPU молотят матрицы, FLOPS-ы летят, всё красиво. На дашборде GPU utilization показывает 90%+, инженер счастлив.

Decode — это когда модель генерирует ответ по одному токену за forward pass. И вот тут начинается боль: каждый токен требует полностью прочитать веса модели из VRAM. На H100 пропускная способность HBM3 примерно 3.35 ТБ/с, и для 70B-модели в FP8 это даёт жёсткий теоретический потолок: даже идеально оптимизированный сервер не сможет выдать больше ~50 токенов/с на одного юзера, просто потому что веса физически не успевают доехать из памяти к ядрам. Вычислители простаивают, ждут данных.

Это и есть memory-bandwidth bound режим. И именно поэтому "GPU utilization 90%" в Datadog обманчив: вычислительные ядра могут быть заняты ожиданием данных, а не полезной работой. Реальную картину лучше смотреть через memory bandwidth, SM occupancy, tokens/sec и latency-метрики, а не через один общий utilization.

Подробнее про то, как это раскладывается по метрикам — в моей предыдущей статье «Как бенчмаркать локальную LLM в 2026: TTFT, TPOT, KV cache, context length и VRAM».

Спекулятивное декодирование решает ровно эту проблему: пока target-модель и так стоит в очереди к памяти, можно потратить часть свободного compute-а на генерацию кандидатов для следующих N токенов и проверить их одним батчированным forward pass'ом target-модели. Если кандидаты угаданы — мы получили несколько токенов за один прогон весов из памяти. Если не угаданы — потеряли compute на драфтер и верификацию. Размен часто в плюс, но только пока ваша нагрузка действительно memory-bound.

Как это работает в одном абзаце

Берём быстрый драфтер — это может быть отдельная мелкая модель, дополнительная "голова" поверх target-а или вообще статистический n-gram-матчер. Драфтер предлагает несколько кандидатов на следующие токены. Target-модель параллельно (одним forward pass'ом, в одном батче) проверяет всю последовательность и говорит: "первые три приняты, четвёртый отвергнут, вместо него выдаю свой". При корректной реализации выход target-модели должен быть алгоритмически lossless относительно обычной авторегрессии. На практике всё равно надо учитывать численные различия, batch effects и особенности sampling-а.

Цена — лишние вычисления на драфтер и на верификацию. На одиночном стриме (батч=1) это очень выгодно: память и так бутылочное горлышко, ядра простаивают, не жалко. На больших батчах вычислительные ресурсы начинают кончаться, и выигрыш постепенно тает. Об этом ниже отдельно.

Карта методов в 2026

Я разделю их по принципу "откуда берутся кандидаты".

N-gram и suffix decoding — без обучения

Самый дешёвый вариант. Драфтер тут не модель, а просто матчинг по тексту.

N-gram ищет повторяющиеся подпоследовательности в самом промпте. Идеально работает на задачах вида "перепиши этот код" или "переведи этот текст" — там модель часто буквально копирует куски ввода. На обычном чате прирост скромный, но не нулевой.

Suffix decoding — следующий шаг: матчит уже не только промпт, но и предыдущие сгенерированные токены, плюс ведёт частотный счётчик и предлагает наиболее вероятные продолжения. Поддерживает древовидное исследование вариантов.

Главное преимущество обоих — никаких весов, никакого обучения, ничего не надо качать. Просто включаете флагом и получаете 1.2–1.5× на подходящих задачах. На неподходящих — почти ноль, но и накладных расходов почти ноль.

В vLLM:

vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --speculative-config '{
    "method": "ngram",
    "num_speculative_tokens": 4,
    "prompt_lookup_min": 2,
    "prompt_lookup_max": 5
  }'

В SGLang это --speculative-algorithm NGRAM. Когда брать: если задача — RAG, рерайтинг, код-ревью или агенты, которые часто пересказывают входные данные.

EAGLE и EAGLE-3 — лёгкая голова поверх target

EAGLE — это уже обученный драфтер, но не отдельная модель, а небольшая надстройка. Идея: вместо того чтобы держать рядом, скажем, 1B-модель и тратить compute на её прогон, мы тренируем компактную "голову", которая берёт скрытые состояния target-модели на промежуточных слоях и предсказывает по ним следующие токены.

EAGLE-3 (NeurIPS 2025) — текущая SOTA-вариация. Голова смотрит на представления target-модели сразу из трёх точек: ранний слой, средний и поздний. Размер драфтерской головы — около 277 МБ, ко-деплоится на той же GPU, лишний VRAM почти не отъедает. По бенчмаркам Spec-Bench — лидер.

Конкретный замер из доков SGLang: EAGLE-3 на Llama-3.1-8B-Instruct на H100 даёт 373 токена/с против 158 токенов/с у голого инференса — это ровно 2.36×.

Минус: EAGLE-3 голову надо где-то взять. Для популярных моделей (Llama-3.1, DeepSeek, Qwen) они уже выложены на HuggingFace. Для нестандартного fine-tune-а придётся обучать самому, и это нетривиальная история — нужен доступ к скрытым активациям target-модели на репрезентативном датасете.

В vLLM:

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --speculative-config '{
    "method": "eagle3",
    "model": "yuhuili/EAGLE3-LLaMA3.1-Instruct-8B",
    "num_speculative_tokens": 5,
    "draft_tensor_parallel_size": 1
  }'

В SGLang:

python3 -m sglang.launch_server \
  --model meta-llama/Meta-Llama-3.1-8B-Instruct \
  --speculative-algorithm EAGLE3 \
  --speculative-draft-model-path jamesliu1/sglang-EAGLE3-Llama-3.1-Instruct-8B \
  --speculative-num-steps 3 \
  --speculative-eagle-topk 4 \
  --speculative-num-draft-tokens 16 \
  --mem-fraction-static 0.7 \
  --cuda-graph-max-bs 8 \
  --dtype float16

Параметры стоит читать так: num-steps — сколько шагов вперёд драфтер генерирует; eagle-topk — сколько вариантов на каждом шаге держим; num-draft-tokens — итоговый бюджет токенов на верификацию (обычно topk^steps + что-то).

Native MTP — когда драфтер встроен в саму модель

MTP (Multi-Token Prediction) в строгом смысле — концептуально другой подход. Здесь драфтер не отдельная сущность, а часть самой архитектуры target-модели. На этапе обучения модель учат предсказывать не только следующий токен, но и токены через 2, 3, 4 шага вперёд. На инференсе эти "MTP-головы" работают как драфтер.

Главная фишка native MTP — драфтер делит часть состояния с target-моделью. Никакого полноценного второго префиксного прохода, меньше параллельных активаций, ниже накладные расходы. В удачном случае это даёт высокий acceptance-rate и приятную latency-картину.

Минус — модель должна быть обучена с MTP с самого начала. Натянуть MTP на готовую модель пост-фактум нельзя.

Из коробки native MTP сейчас встречается у отдельных семейств вроде DeepSeek-V3/V2, Qwen3-Next, MiMo, ERNIE, LongCat Flash, Pangu Ultra MoE. В vLLM-конфиге это методы deepseek_mtp, qwen3_next_mtp, mimo_mtp, ernie_mtp и так далее, либо обобщённый mtp. Gemma 4 лучше держать отдельной категорией: Google выпустил для неё assistant-драфтеры, которые используются в speculative decoding как малые draft-модели, но это не то же самое, что встроенные MTP-головы внутри target.

vllm serve deepseek-ai/DeepSeek-V3 \
  --speculative-config '{
    "method": "deepseek_mtp",
    "num_speculative_tokens": 2,
    "disable_padded_drafter_batch": "False"
  }'

В SGLang MTP заводится через тот же EAGLE-флаг:

python3 -m sglang.launch_server \
  --model XiaomiMiMo/MiMo-7B-RL \
  --speculative-algorithm EAGLE \
  --speculative-num-steps 1 \
  --speculative-eagle-topk 1 \
  --speculative-num-draft-tokens 2 \
  --mem-fraction-static 0.7

Это не баг — SGLang переиспользует EAGLE-пайплайн для MTP, потому что архитектурно они близки.

DFLASH — драфтер на block diffusion (свежак февраля 2026)

Самый молодой метод в этом списке и, похоже, главный шум весны. Paper вышел в феврале 2026 от лаборатории Z Lab, а дальше вокруг него быстро появились интеграции в serving-стек.

Главная идея — добить одно неприятное ограничение EAGLE. Какая бы лёгкая EAGLE-голова ни была, она всё равно автогрессивная: чтобы предложить 4 токена вперёд, она должна сделать 4 последовательных шага. Это вынуждает держать драфтерскую архитектуру буквально однослойной — иначе её собственный latency сожрёт весь выигрыш от спекуляции. А однослойный драфтер — это потолок по качеству предсказаний и, как следствие, по acceptance-rate.

DFLASH ставит на место драфтера block diffusion model. Диффузионная модель генерирует блок draft-токенов одним параллельным forward pass. То есть стоимость драфта не обязана расти линейно с числом предлагаемых токенов, как у автогрессивного EAGLE. Это даёт сразу два бонуса: можно сделать драфтер глубже и одновременно предлагать больше токенов вперёд, если под target-модель есть подходящий DFlash checkpoint.

Вторая интересная штука — как DFLASH забирает информацию из target-модели. EAGLE-3 берёт скрытые состояния target из последнего слоя (ну, или из трёх для EAGLE-3) и подаёт их на вход драфтеру. DFLASH идёт дальше: делает feature fusion из множества равномерно засемплированных слоёв target-модели и инжектит результат напрямую в Key/Value-проекции каждого слоя драфтера, кладёт в KV-кеш. То есть кондиционирование на target живёт через всю глубину драфтера, а не один раз на входе. Авторы называют это "persistent conditioning" и связывают именно с этим высокий acceptance.

Что показывают замеры:

  • Qwen3-8B на B200 в SGLang, concurrency=1: до 5.1× ускорения
  • Тот же Qwen3-8B при concurrency=32: 2.8× — заметная деградация с ростом батча, но не катастрофа
  • Qwen3.5-9B: до 4.4× против обычной авторегрессии
  • На reasoning-моделях с включённым thinking mode: примерно 4.5× — спекуляция вообще лучше всего окупается на длинных reasoning-цепочках
  • На math, code и chat бенчмарках стабильно обходит EAGLE-3 — что нечасто увидишь, EAGLE-3 был SOTA полгода

Запуск в SGLang выглядит так:

python -m sglang.launch_server \
  --model-path Qwen/Qwen3-Coder-30B-A3B \
  --speculative-algorithm DFLASH \
  --speculative-draft-model-path z-lab/Qwen3-Coder-30B-A3B-DFlash \
  --speculative-num-draft-tokens 16 \
  --attention-backend fa3 \
  --mem-fraction-static 0.75 \
  --trust-remote-code

Отдельно следите за версией SGLang/vLLM и README конкретного DFlash checkpoint-а: вокруг этого метода интеграции меняются быстро, и часть флагов живёт в экспериментальных ветках раньше, чем попадает в стабильную документацию.

Ограничения, которые надо знать заранее:

  • Готовые драфтеры сначала появились вокруг Qwen3 / Qwen3.5 / Qwen3-Coder, но список быстро расширяется: в репозитории Z Lab уже есть checkpoints для Gemma 4, Llama-3.1-8B-Instruct, gpt-oss и других моделей. Поэтому перед выбором метода надо смотреть не только семейство target-модели, но и наличие конкретного matching draft checkpoint.
  • Эталонные бенчмарки часто сделаны на B200/современных attention backend-ах. На H100 цифры могут быть скромнее, на consumer-картах типа 4090/3090 — нужно проверять отдельно. Публичные community-цифры полезны как ориентир, но не как основа для capacity planning.
  • По dtype и quantization драфтера нужно смотреть конкретный checkpoint: часто стартовая точка — BF16, но это не универсальная гарантия для всех портов и backend-ов.
  • В SGLang DFLASH уже есть отдельным алгоритмом --speculative-algorithm DFLASH. В vLLM ситуация быстро движется: в актуальной документации dflash уже фигурирует как метод --speculative-config, а Z Lab пишет про core DFlash support в vLLM v0.20.1+ с временными ветками/образами для отдельных архитектур. Поэтому старое правило "DFLASH только в SGLang" уже не стоит держать как абсолют.

Когда брать: если под вашу target-модель есть DFlash checkpoint, вы готовы жить с ограничениями backend-а и у вас latency-sensitive decode при невысокой параллельности — DFLASH стоит проверять первым. Особенно интересно на reasoning-задачах, где длинный decode даёт спекуляции больше пространства для окупаемости.

Что было с Gemma 4 — поучительная история апреля 2026

Эта часть — не маркетинговый разбор, а история реального инцидента, потому что она показывает, как именно живёт спекулятивное декодирование в 2026.

3 апреля Google релизнул Gemma 4. Вокруг релиза почти сразу началась путаница: в обсуждениях много говорили про MTP, но публичные веса target-моделей не давали простого "включил флаг и получил native MTP" сценария. Для vLLM, SGLang и llama.cpp это означало обычный авторегрессивный decode, пока не появились совместимые драфтеры и фиксы в serving-стеке.

Вторая сложность — Gemma 4 использует гибридную attention: чередует sliding-window и full-attention слои. Это делает speculative decoding менее прямолинейным, потому что драфтер, KV-cache и attention backend должны одинаково понимать архитектуру target-модели. Вокруг этого быстро появились EAGLE/assistant/DFlash-чекпойнты и serving-фиксы, но сама история хорошо показывает цену "новая архитектура + day-0 serving".

И вот в начале мая Google официально выложил assistant-драфтеры под Apache 2.0 для Gemma 4 (E2B, E4B, 26B-A4B, 31B) — со словами про speedup до 2× и без потерь качества относительно стандартной генерации. То есть примерно месяц комьюнити и vendor-ы закрывали практический gap между "модель вышла" и "спекулятивное декодирование удобно запустить".

Что из этого стоит вынести: спекулятивное декодирование — это не "включил флаг и всё работает". Оно тесно связано с архитектурой attention в target-модели, с тем, как организован KV-кеш, и с особенностями конкретной версии serving-движка. Если завтра выходит новый MoE с какой-нибудь экзотичной маршрутизацией экспертов — ждите ещё один такой же раунд багфиксов.

Где спекуляция начинает разваливаться

Главный миф — "включил EAGLE-3, получил 2.5× и счастлив всегда". В реальности есть несколько мест, где скорость деградирует или вообще пропадает.

1. Большой батч

Спекулятивное декодирование выгодно, пока вы упираетесь в пропускную способность памяти. На батче=1 это всегда так. На батче 4–8 обычно тоже. На батче 64+ — уже не факт. С ростом батча нагрузка на вычисления в decode растёт линейно (нужно считать attention для каждой позиции каждого юзера), а нагрузка на шину памяти остаётся примерно постоянной — веса читаются один раз на батч. В какой-то точке режим переключается с memory-bound на compute-bound, и любой драфтер начинает только мешать: он добавляет лишние вычисления, которые теперь бьют по уже занятым ядрам.

Это, кстати, объясняет, почему в анонсах speculative decoding часто подчёркивают low-latency, on-device или low-QPS сценарии. Цифра из локального запуска на Apple Silicon или одиночного B200 — не то же самое, что серверный инференс на H100 с 256 параллельными пользователями.

2. Низкий acceptance-rate

Если задача такая, что target-модель часто отвергает кандидатов от драфтера (например, генерация очень разнообразного креативного текста), вы платите compute за драфт и за отвергнутую верификацию, а получаете немного токенов за прогон. Эвристики типа той, что в HF Transformers, динамически подкручивают num_speculative_tokens вниз при rejection — но они не могут сотворить чудо. Если задача плохо предсказуема — спекуляция работает хуже.

Хороший показатель в проде — acceptance length (среднее число токенов, принятых за один draft-verify цикл). На EAGLE-3 здоровый acceptance length для чата это 2.5–3.5. Если он у вас 1.5 — что-то сломано: драфтерская модель не подходит, температура слишком высокая, или вы упёрлись в compute.

3. Pipeline parallelism

В vLLM ≤0.15.0 спекулятивное декодирование несовместимо с pipeline parallelism. Если вы раскатили модель на несколько GPU через PP (а не TP), включить спекуляцию нельзя. Это известное ограничение, которое чинят, но проверяйте релиз-ноуты вашей версии.

Если вы как раз планируете multi-GPU/multi-node инференс — у меня есть отдельная подробная статья: «Распределенный запуск LLM на нескольких GPU с помощью Ray и vLLM».

4. Нестабильные logprobs

Документация vLLM прямо предупреждает: при включённом спекулятивном декодировании logprobs не гарантированы стабильными между запусками и между размерами батча. Если у вас downstream-логика, которая на logprobs опирается (например, классификатор поверх токенов или constrained decoding с определёнными правилами) — это надо тестировать отдельно.

5. KV-кеш и память

EAGLE-голова сама по себе небольшая (~277 МБ), но во время верификации в KV-кеш заходит сразу несколько кандидатов на каждый токен. Эффективный размер KV вырастает в num_speculative_tokens раз для проверяемой части. На длинном контексте это может вытолкнуть вас в OOM, который без спекуляции бы не случился. Стандартное лекарство — снизить gpu_memory_utilization или max_num_seqs.

Подробно про KV-кеш и его размер — в «KV-cache в моделях transformers».

Практический чеклист: как выбрать метод под свой кейс

Не претендую на универсальную истину, но вот как я расставил бы приоритеты.

Если у вас батч=1 или локальный запуск с малой параллельностью:

  1. Если под вашу модель есть DFlash checkpoint и backend его поддерживает — первым проверьте DFLASH.
  2. Если модель из native MTP-семьи (DeepSeek-V3, Qwen3-Next, MiMo и похожие) — проверяйте MTP. Если это Gemma 4 — смотрите на assistant-драфтеры или DFlash/EAGLE-чекпойнты под конкретную версию модели.
  3. Иначе — EAGLE-3, если есть готовая голова.
  4. Если головы нет и обучать нечем — n-gram. Скромно, но бесплатно.

Если у вас сервинг с высокой параллельностью (батч >32):

  1. Сначала измерьте, в каком вы режиме. Если уже compute-bound — спекуляция, скорее всего, не поможет, а то и навредит.
  2. Если всё ещё memory-bound — n-gram/suffix как самый дешёвый по compute вариант.
  3. EAGLE-3 — только если acceptance-rate в проде стабильно выше 2.5.

Если у вас агенты или RAG с большим повторением промпта:

n-gram даёт самый предсказуемый прирост, потому что много токенов буквально копируется из инпута.

Если вы не знаете, в каком вы режиме:

Замерьте отдельно для своей нагрузки. На vLLM есть встроенные prometheus-метрики vllm:spec_decode_num_accepted_tokens_total и vllm:spec_decode_num_draft_tokens_total — отношение даёт тот самый acceptance length. На SGLang — аналогичные spec_* метрики.

Про сам процесс измерения и подводные камни — в «Как бенчмаркать локальную LLM в 2026».

Сравнительная таблица

МетодУскорениеДоп. VRAMНужно обучатьЛучше всего для
N-gram1.1–1.5×~0НетRAG, рерайтинг, код
Suffix1.2–1.6×~0НетТо же + длинные сессии
EAGLE-32.0–2.5×~300 МБДа (есть готовые)Универсал, батч ≤16
Native MTP (DeepSeek, Qwen3-Next, MiMo)2.0–3.0×Низкая/зависит от реализацииТолько при тренировке targetСовременные MTP-модели, локальный запуск
Gemma 4 assistant-драфтерыДо ~2×Нужен отдельный drafterНет, если берёте готовый checkpointGemma 4, локальный и low-QPS inference
DFLASH3.0–5.1×Зависит от draft checkpointДа (есть готовые для ряда моделей)Matching checkpoint + SGLang/vLLM, reasoning, B200/H100

Цифры — медианные по бенчмаркам и докам vendor-ов на конец апреля – начало мая 2026. Под вашу модель и нагрузку могут отличаться в 1.5 раза в обе стороны.

Частые вопросы

Спекулятивное декодирование меняет качество ответа?

В идеальной формулировке — нет: target-модель верифицирует draft-токены и сохраняет распределение обычной авторегрессии. В реальном serving-е могут всплывать численные различия, batch effects, sampling randomness и особенности конкретной реализации. Поэтому для greedy/temperature=0 обычно ждём совпадения, а для stochastic sampling лучше сравнивать распределение и метрики, а не один конкретный текст.

А почему тогда у меня ответы стали другими после включения EAGLE?

Скорее всего, у вас изменились logprobs из-за другого размера батча или из-за того, что спекулятивный путь включил другую реализацию attention. Если используете temperature>0 — изменилась последовательность семплирования. Это не потеря качества, а другая выборка из того же распределения.

Сколько токенов выставлять в num_speculative_tokens?

Дефолт 4–5 — нормальная отправная точка. Дальше смотрите на acceptance length: если он близок к num_speculative_tokens — можно увеличить. Если в 2 раза меньше — уменьшайте. Слишком большое значение тратит compute впустую и ухудшает latency на rejection-кейсах.

Можно ли использовать спекуляцию с квантованной моделью (FP8, INT4)?

Да, и часто это даёт лучший combined результат — квантование снижает bandwidth-нагрузку на target, спекуляция уменьшает количество прогонов через память. Но проверьте, что квантованная версия драфтера (если он отдельный) совместима по dtype с target.

А если у меня DeepSeek-V3 и я уже использую native MTP — есть ли смысл сверху накатывать EAGLE-3?

Обычно нет. Native MTP уже встроен в архитектуру и использует близкое состояние target-модели. Накатывать сверху ещё один драфтер — это два слоя спекуляции, которые будут конкурировать за compute и acceptance.

А почему DFLASH-цифры из paper иногда не воспроизводятся у меня?

Потому что эталонные бенчмарки завязаны на конкретное железо, attention backend, batch/concurrency и draft checkpoint. На H100 вместо B200 вы можете недосчитаться ощутимой части. На consumer-карте — ещё больше. Плюс некоторые публичные цифры — это проекция от paper "DFLASH в 2.5× быстрее EAGLE-3" на ваш baseline, а не прямой замер. Перед capacity planning меряйте на своей нагрузке и своём железе, без вариантов.

Что с TensorRT-LLM, не пропустил ли я его?

Не пропустили. TensorRT-LLM поддерживает EAGLE и Medusa и обычно выдаёт лучшую сырую скорость на железе NVIDIA, но за это вы платите менее гибким развёртыванием и привязкой к одному вендору. В open-source стеке vLLM и SGLang остаются главными игроками, и именно они получают day-0 поддержку новых драфтерских архитектур — как только что было с Gemma 4.

Что почитать рядом


Заинтересовало? Больше практических разборов про LLM, инференс и AI-инфраструктуру — в моём Telegram-канале @fuckup_files.

Мой тг · про факапы@fuckup_files
Спекулятивное декодирование в vLLM и SGLang: MTP, EAGLE-3 и n-gram в 2026