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

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

TL;DR

Если у вас стоит vLLM или SGLang и вы упёрлись в задержки на decode-фазе — спекулятивное декодирование (speculative decoding) в 2026 это уже не экспериментальная фича, а скорее обязательная опция. Сейчас в проде работают четыре подхода: n-gram/suffix (без обучения, чистая алгоритмика), EAGLE-3 (лёгкая голова, смотрит в активации target-модели, ~2–3× ускорения), MTP (Multi-Token Prediction — часть архитектуры самой модели, делит KV-кеш с target) и совсем свежий DFLASH (block-diffusion драфтер от Z Lab — до 5× в SGLang на Qwen3, обходит EAGLE-3). На прошлой неделе Google официально выпустил MTP-драфтеры для всей семьи Gemma 4 — E2B, E4B и 26B-MoE — с заявленными до 3× ускорения и поддержкой day-0 в vLLM, SGLang, MLX, transformers и Ollama. А в феврале вышла статья про DFLASH (arXiv:2602.06036), которая ломает потолок автогрессивных драфтеров за счёт диффузии. Ниже разбираю, какой метод когда брать, как это завести в обоих движках и почему рост батча убивает спекуляцию быстрее, чем кажется.

Зачем вообще ускорять 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 обманчив — компьютные ядра загружены прогрессивным ожиданием, а не работой. Прокачка через шину памяти при этом обычно болтается в районе 15–20%.

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

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

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

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

Цена — лишние вычисления на драфтер и на верификацию. На одиночном стриме (батч=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 + что-то).

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

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

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

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

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

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 (UC San Diego), интеграцию в SGLang довела команда Modal.

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

DFLASH ставит на место драфтера block diffusion model. Диффузионная модель генерирует все N токенов за один параллельный forward pass, независимо от того, чему равно N. То есть стоимость драфта не растёт линейно с числом предлагаемых токенов, как у автогрессивного EAGLE — она почти плоская. Это даёт сразу два бонуса: можно сделать драфтер глубже (типичная конфигурация — 5 слоёв против одного у EAGLE-3) и одновременно предлагать больше токенов вперёд (16 — рабочая величина), и при этом latency драфтерской фазы остаётся ниже, чем у однослойного EAGLE-3 с 8 токенами. Глубже модель, больше токенов, меньше времени.

Вторая интересная штука — как 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

Опционально — можно включить экспериментальный Spec-v2 overlap scheduler, он даёт ещё немного сверху за счёт перекрывания планирования с исполнением:

SGLANG_ENABLE_SPEC_V2=1 \
SGLANG_ENABLE_DFLASH_SPEC_V2=1 \
SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1 \
python -m sglang.launch_server ...

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

  • Готовые драфтеры пока есть только для семьи Qwen3 / Qwen3.5 / Qwen3-Coder (z-lab/Qwen3.5-4B-DFlash, z-lab/Qwen3.5-9B-DFlash, z-lab/Qwen3-Coder-30B-A3B-DFlash, z-lab/Qwen3.5-35B-A3B-DFlash). Для Llama, DeepSeek, Mistral, Gemma — придётся либо ждать, либо тренировать самому.
  • Эталонные бенчмарки сделаны на B200 с FlashAttention-4. На H100 цифры заметно скромнее, на consumer-картах типа 4090/3090 — нужно проверять отдельно. Есть community-портации в GGUF, на одиночной 3090 для Qwen3.5-27B GGUF получают около 207 токенов/с (HumanEval), это ~3.4× против обычного авторегрессивного декодинга.
  • Веса драфтеров пока только в BF16.
  • В vLLM нативной поддержки DFLASH ещё нет (по состоянию на начало мая 2026) — это эксклюзив SGLang. Для vLLM-стека ваш реалистичный потолок пока EAGLE-3 или MTP, если повезло с моделью.

Когда брать: если у вас в проде Qwen3 / Qwen3.5 / Qwen3-Coder, поднят SGLang и под рукой H100 или B200 — заводите DFLASH без раздумий. Особенно выгодно на reasoning-задачах — там этот подход раскрывается сильнее всего.

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

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

3 апреля Google релизнул Gemma 4. В тот же день комьюнити обнаружило две неприятные вещи. Первая — в архитектуре модели заявлены MTP-головы, но в публичных весах их вырезали. То есть из коробки ни vLLM, ни SGLang, ни llama.cpp не могли запустить спекулятивное декодирование, и пользователи получили обычный авторегрессивный decode со всей его memory-bandwidth тоской.

Вторая — Gemma 4 использует гибридную attention: чередует sliding-window и full-attention слои. Это сломало вообще все существующие пайплайны спекулятивного декодирования, рассчитанные на однородную attention. Команда Thoughtworks к 6 апреля выкатила EAGLE-3 голову для Gemma-4-31B (thoughtworks/Gemma-4-31B-Eagle3), но чтобы её завести, потребовалось три багфикса в serving-стеке и отдельное решение проблемы с dual-KV-cache memory leak — потому что sliding-window и full-attention хранят кеши по-разному, и драфтер с ними не дружил без шаманства. Заявленное ускорение — 1.72×.

Параллельно RedHat выложил RedHatAI/gemma-4-31B-it-speculator.eagle3 — тот же EAGLE-3, но уже под instruction-tuned вариант.

И вот 5 мая Google официально релизнул MTP-драфтеры под Apache 2.0 для всей семьи Gemma 4 — со словами "до 3× ускорения, без потерь качества, day-0 поддержка в vLLM, SGLang, MLX, transformers, Ollama". То есть месяц комьюнити закрывало дыру, которую исходно оставил релиз.

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

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

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

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

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

Это, кстати, объясняет, почему Google в анонсе Gemma 4 MTP отдельно подчёркивает цифру ~2.2× ускорения на Apple Silicon при батче 4–8. Эта цифра не для серверного инференса на 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. Если у вас Qwen3 / Qwen3.5 / Qwen3-Coder и стоит SGLang — берите DFLASH. Это лучшее, что сейчас доступно из коробки, и с большим отрывом.
  2. Если модель из MTP-семьи (Gemma 4, DeepSeek-V3, Qwen3-Next, MiMo) — берите MTP. Лучший acceptance, минимум возни.
  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
MTP (Gemma 4, DeepSeek)2.0–3.0×~0 (часть target)Только при тренировке targetСовременные MTP-модели, локальный запуск
DFLASH3.0–5.1×~зависит от моделиДа (готовы для Qwen3/3.5)Qwen3 + SGLang, reasoning, B200/H100

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

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

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

Математически — нет. Target-модель верифицирует каждый токен и принимает только те, что совпадают с её собственным распределением. То, что вы получите на выходе, идентично обычному авторегрессивному генерированию (с точностью до численных артефактов на больших батчах).

А почему тогда у меня ответы стали другими после включения 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 и я уже использую MTP — есть ли смысл сверху накатывать EAGLE-3?

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

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

Потому что эталонные бенчмарки делались на B200 с FlashAttention-4 и Spec-v2 overlap. На H100 без FA4 вы недосчитаетесь ощутимой части. На 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