Бенчмарк · DeepSeek-V4.1-Flash-1017 · 8×B300

Больше 600K токенов/с: DeepSeek 4.1 Flash на 8×B300, vLLM против DeepInfer

DeepSeek открыла исходники DeepInfer — движка, на котором работает её собственный сервис. Мы сравнили его с vLLM на одном узле 8×B300 с DeepSeek-V4.1-Flash-1017: как модель раскладывается по памяти, на что способен каждый движок на выводе и префилле и почему DeepInfer отстаёт на одном узле.

Обновлено 21 октября. В первой версии статьи DeepInfer работал с настройками по умолчанию. В тот же день мы перезапустили его с настройками, которые DeepSeek рекомендует для V4.1, включая bounded replay; в таблицах оба прогона, а в каждом сравнении — лучший из двух.
658Kпрефилл-токенов/с на одном узле, vLLM, промпты 8K
55,0Kвыходных токенов/с на 1K → 1K; 65,0K на коротких промптах
2–4×vLLM быстрее DeepInfer на декоде, в 8× на префилле
88 сwarmctl up против холодного старта за 930 с
DeepSeek-V4.1-Flash-1017 · один узел 8×B300 · 2048 запросов (512 для 32K)vLLM против DeepInfer
vLLM DeepInfer decode · 128 → 1K 65.0K output tok/s 36.4K 1.8× decode · 1K → 1K 55.0K output tok/s 27.9K 2.0× decode · 8K → 1K 29.5K output tok/s 7.3K 4.0× prefill · 8K prompts 658K input tok/s 72K 9.2× prefill · 32K prompts 590K input tok/s 72K 8.2×

01 · ЗАЧЕМНовый движок инференса: DeepInfer

В октябре DeepSeek открыла исходники DeepInfer — по её словам, «высокопроизводительного фреймворка инференса, на котором работают крупные продакшен-сервисы DeepSeek». В нём ядра, которые DeepSeek публикует и отдельно (FlashMLA, DeepGEMM, DeepEP, DeepSelect), data-parallel attention с expert-parallel MoE и спекулятивный декодинг DSpark. Для этой же модели на B300 есть рецепт vLLM — того самого движка, с которого начинался DeepInfer, — и он использует в основном те же ядра.

Отсюда очевидный вопрос: насколько они расходятся на одном узле 8×B300 — самой крупной единице, которую арендует большинство команд? Мы запустили DeepSeek-V4.1-Flash-1017 на обоих движках и выжали из узла максимум и по выводу, и по префиллу.

02 · ДВИЖКИvLLM и DeepInfer: в чём разница

Оба движка запускают одну модель в основном одними и теми же ядрами и масштабируют её одинаково: attention копируется на каждую GPU, эксперты делятся между GPU. Разница — в назначении и в том, что идёт в комплекте. vLLM — универсальный движок: поставил и обслуживаешь запросы. DeepInfer — ядро собственного сервиса DeepSeek, выпущенное без сервиса вокруг него.

vLLMDeepInfer
Что этоУниверсальный open-source движок и сервер инференсаПродакшен-движок DeepSeek, открытый для одной модели
МоделиСотни открытых моделей, эта в том числеТолько DeepSeek-V4.1-Flash
ЖелезоNVIDIA, AMD, TPU и другоеNVIDIA SM100 (B200, B300, GB200) и Huawei Ascend 950
ИнтерфейсHTTP-сервер с OpenAI API, tool calls и reasoning-парсерамиPython API, офлайн-батчи (LLM.generate), сервера нет
УстановкаDocker-образ или pip-пакетСборка из исходников: GCC 13, CUDA 13 с NVCC 13.1 или новее
Сильные стороныPrefix caching, structured outputs, LoRA, раздельный префилл через KV-коннекторыDSpark с проверкой по уверенности, batch-invariant инференс, детерминированный сэмплинг
Не входит—Система KV-кэша, разделение префилла и декода, балансировка
Рассчитан наЛюбой масштаб, от одной GPU4 GPU на префилл и 64 на декод в одном NVLink-домене

03 · СТЕНД8×B300, одна модель, два движка

Конфигурация
GPU8× NVIDIA B300 SXM6 AC, по 275 GB HBM3e, NVLink 5 через NVSwitch
Хост192 vCPU, 2,7 TB RAM, шесть локальных NVMe в RAID 0 (21 TB)
Модельdeepseek-ai/DeepSeek-V4.1-Flash-1017, 476 GB: эксперты в FP4, остальное в FP8, две таблицы Engram по 384M строк
vLLM0.31.1rc1, образ из рецепта vLLM для B300, драйвер NVIDIA 610.57.04; DEP8, 256 последовательностей и шаг 8K токенов на ранг, DSpark с 5 черновыми токенами
DeepInferdeepseek-ai/DeepInfer на 8b16149 (20 октября), собран из исходников с CUDA 13.2; EP8 × DP8, DSpark с 5 черновыми токенами, офлайн API; B300 SXM6 PC, драйвер 580
warmctlv2.0.0-beta.10, снимок deepseek-v41-flash-1017:8xb300ac-275gb-610.57.04, 851 GiB
Нагрузкаvllm bench serve через OpenAI API: случайные токены, фиксированная длина ответа (--ignore-eos), запросов вдвое больше конкурентности; DeepInfer получает те же промпты в процессе через LLM.generate

Цифры vLLM получены на движке, восстановленном warmctl, и сравниваются с тем же рецептом, запущенным нативно днём раньше на другом узле, B300 SXM6 PC. Случайные промпты — худший случай для спекулятивного декодинга: DSpark принял 19% черновых токенов на 1K и 12% на 8K. На реальном тексте принимается больше, так что все цифры декода здесь — нижняя граница.

04 · РАСКЛАДКАКак DEP8 раскладывает модель по восьми GPU

Модель — mixture of experts: 384 маршрутизируемых эксперта и один общий на слой, по 6 маршрутизируемых на токен. Attention — разреженный вариант MLA: скользящее окно в 128 токенов плюс индексатор, который выбирает 512 самых значимых предыдущих токенов, поэтому стоимость шага почти не растёт с контекстом. Две таблицы Engram — n-граммные эмбеддинги, которые модель ищет в таблице вместо вычислений, — занимают 40% чекпоинта.

Рецепт vLLM запускает её как DEP8: data-parallel attention, expert-parallel MoE. Каждая GPU держит свою копию attention со своими запросами и KV-кэшем и 48 из 384 экспертов. В каждом MoE-слое GPU обмениваются токенами через NVSwitch, чтобы каждый токен попал к своим шести экспертам, а затем возвращают результаты обратно.

DEP8 на 8×B3008 реплик · по 1/8 экспертов
GPU 0 attention KV cache 160 GiB 48 experts GPU 1 attention KV cache 160 GiB 48 experts GPU 2 attention KV cache 160 GiB 48 experts GPU 3 attention KV cache 160 GiB 48 experts GPU 4 attention KV cache 160 GiB 48 experts GPU 5 attention KV cache 160 GiB 48 experts GPU 6 attention KV cache 160 GiB 48 experts GPU 7 attention KV cache 160 GiB 48 experts NVLink · NVSwitch: tokens to their experts and back (all-to-all), every MoE layer Engram 1/8 Engram 1/8 Engram 1/8 Engram 1/8 Engram 1/8 Engram 1/8 Engram 1/8 Engram 1/8 host RAM, pinned: each rank keeps 1/8 of the Engram rows (23.6 GiB) and gathers the rows it needs from the others every step

Места на каждой GPU остаётся много. Весам одного ранга нужно 46 GiB; профиль резервирует фиксированные 160 GiB под KV-кэш в формате NVFP4 MLA от DeepSeek, и vLLM насчитывает там место для 52 миллионов токенов на ранг — это 264 запроса с полным контекстом 196K. Узел упирается не в память, а в 256 одновременных последовательностей на ранг, 2048 на узел.

одна B300 · 268,6 GiBранг DEP8
weights: 45.8 GiB 46 GiB KV cache: 160.0 GiB 160 GiB CUDA graphs: 8.0 GiB buffers and headroom: 54.7 GiB 55 GiB weights · 45.8 GiB KV cache · 160.0 GiB CUDA graphs · 8.0 GiB buffers and headroom · 54.7 GiB

05 · vLLM НА ПОЛНОЙ НАГРУЗКЕ658K токенов/с на вход, 65K на выход

Начнём с префилла — главная цифра здесь. Мы отправляли промпты с одним выходным токеном, так что узел только читает их. При 2048 одновременных промптах по 8K рецепт vLLM обрабатывает 658 259 токенов/с, около 82K на GPU; при 512 промптах по 32K — 589 924. Длинные промпты дешевле в пересчёте на токен: промпты по 1K при той же конкурентности дают 462 366.

один выходной токен на запрос · 8K и 32K: рецепт нативно, 1K: восстановленный движокпрефилл tok/s
8K prompts · 2,048 concurrent 658K tok/s 32K prompts · 512 concurrent 590K tok/s 1K prompts · 2,048 concurrent 462K tok/s

На декоде 2048 одновременных запросов дают 64 990 выходных токенов/с на коротких промптах и 54 985 на 1K → 1K — около 6900 на GPU при 29 мс на токен. С промптами по 8K вывод падает до 29 500, но параллельно узел читает 235 996 токенов промптов в секунду. 2048 — это и потолок: 256 последовательностей на ранг. При 4096 запросах лишние ждут в очереди, а вывод остаётся на 55 506.

восстановленный движок · 2048 одновременных запросоввыход tok/s
128 → 1K 65.0K tok/s 1K → 1K 55.0K tok/s 8K → 1K 29.5K tok/s 2K → 128 19.8K tok/s
Вход → выходКонкурентностьВыход, tok/sВсего, tok/sНативно, выходTPOT
128 → 1K2 04864 99073 114—24.8 ms
1K → 1K2 04854 985109 96953 89228.8 ms
1K → 1K4 096——55 50632.8 ms
8K → 1K2 04829 500265 49629 58052.8 ms
2K → 1282 04819 753335 801—74.4 ms

Цифры декода — с движка, восстановленного warmctl; пики префилла — с рецепта, запущенного нативно накануне на B300 SXM6 PC. Там, где мы мерили одну и ту же точку на обоих, расхождение меньше 2%: 54 985 против 53 892 на 1K → 1K, 29 500 против 29 580 на 8K → 1K. Восстановление возвращает движок таким, каким он был, вместе с CUDA-графами и автотюненными ядрами. При вдвое меньшей конкурентности восстановленный движок дал на префилле 554K и 557K.

Наш первый восстановленный движок выдавал всего 37,2K выходных токенов/с на 1K → 1K. Он захватывал CUDA-графы до 1536 токенов, а рецепт — до 8190. Под нагрузкой большинство шагов смешивают декод с куском промпта, а в data-parallel режиме каждый ранг дополняет шаг до самого большого, поэтому такие шаги шли без графов. Графы до 8192 токенов подняли его до 55,0K (изменение профиля).

06 · DEEPINFERСоздан для стойки, запущен на узле

Запустить DeepInfer оказалось дольше, чем его измерить. Он собирается из исходников и требует NVCC 13.1 или новее — мы взяли его из колёс CUDA 13.2 от NVIDIA. Строки Engram он гоняет между GPU через GPU-initiated networking в NCCL, которому даже на одном узле нужно RDMA-устройство: на VM с 8×B200 без рабочего RDMA он не стартовал вовсе (DeepEP#789), а на этой B300 заработал с драйвером 580, но не с 610.

Открытая версия — только офлайн: HTTP-сервера нет, запросы идут через LLM.generate. Мы отдали ему те же нагрузки со случайными токенами прямо в процессе, и это избавило его от API-сервера, токенизатора и сети, которые входят в цифры vLLM. Прогонов было два: с настройками по умолчанию и с настройками, которые для V4.1 задают README и примеры DeepSeek.

Настройка DeepInferНастроенный прогон (21 октября)Почему
ПараллелизмEP8 × DP8: восемь движков, эксперты на всех восьми GPUкак у DeepSeek
Bounded replayYOCO_PREFILL_TRUNCATION_MODE=compact, 140 токеновREADME: настройка по умолчанию для V4.1
DSpark5 черновых токенов, проверка по уверенности (ENABLE_MTP_VARLEN=1)README
Эксперты32 резервных эксперта, EPLB каждые 30 шаговпримеры DeepSeek
Engramтаблицы в памяти GPU (ENGRAM_FORCE_GPU_STORAGE=1)у B300 есть место; без 190 GiB закреплённой памяти хоста
Батчинг256 последовательностей и шаг префилла 32K токенов на рангте же лимиты, что у vLLM; шаг по умолчанию в бенчмарке DeepSeek
Хостдрайвер 580, CUDA 13.2, memlock без лимита, RDMA-сетевухина драйвере 610 DeepInfer не стартовал

На бумаге главная настройка — bounded replay. Слои V4.1 с 21-го по 39-й переиспользуют KV-кэш четырёх более ранних слоёв и смотрят через окно в 128 токенов, поэтому ранние токены промпта через них проходить не должны; с этой настройкой DeepInfer пропускает через эти слои только последние 140 токенов каждого промпта.

8×B300 · 2048 одновременных запросоввыход tok/s
vLLM DeepInfer 128 → 1K 65.0K out tok/s 36.4K out tok/s 1K → 1K 55.0K out tok/s 27.9K out tok/s 8K → 1K 29.5K out tok/s 7.3K out tok/s

На декоде vLLM впереди везде: в 1,8 раза на коротких промптах, в 2,0 раза на 1K → 1K и в 4,0 раза на 8K → 1K, где каждый запрос сначала префиллит 8K токенов. Лучший результат DeepInfer на 1K → 1K, 27 906 выходных токенов/с, дал прогон по умолчанию; настроенный выдал 21 161 — скорее всего, из-за EPLB, который каждые 30 шагов переставляет экспертов, а случайные токены, и так равномерно распределённые по экспертам, этого не окупают.

один выходной токен на запроспрефилл tok/s
vLLM DeepInfer 8K prompts 658K tok/s 72K tok/s 32K prompts 590K tok/s 72K tok/s

На префилле разрыв больше. Настроенный DeepInfer читает 71 632 токенов/с промптов по 8K и 71 875 промптов по 32K, около 9K на GPU; vLLM — 658 259 и 589 924. Bounded replay поднял результат DeepInfer на 32K на 11%, с 64 847, а не вдвое, как можно было ждать по пропущенным слоям, — значит, время уходит в другое место.

НагрузкаКонкурентностьvLLMDeepInfer, настроенныйDeepInfer, по умолчаниюvLLM ÷ лучший
128 → 1K2 04864 99036 381—1,8×
1K → 1K2 04854 98521 16127 9062,0×
1K → 1K4 09655 506—26 2942,1×
8K → 1K2 04829 5007 2976 9334,0×
префилл 8K2 048658 25971 632—9,2×
префилл 32K512589 92471 87564 8478,2×

Собственная кривая префилла DeepSeek показывает 82–89K токенов/с на GPU — примерно в девять раз больше нашего. Она получена в другой конфигурации: 4 GPU на группу префилла, принудительная балансировка экспертов (DEEPINFER_FORCE_LOAD_BALANCE=1 в их скрипте бенчмарка) и префилл 4K-хвостов поверх закэшированного контекста. Ещё на нашем узле не было MLOPart — им DeepGEMM размещает буферы MoE на нужной половине кристалла B300, а драйвер 580 его не поддерживает. Воспроизвести цифру DeepSeek на одном узле мы не смогли, и префилл DeepInfer не профилировали.

README прямо говорит, подо что сделан DeepInfer: декод на 64 GPU и префилл на 4, «каждый в одном scale-up домене», префилл и декод на разных машинах. То, что связывает такой деплой — система KV-кэша, разделение префилла и декода, балансировка, — в открытую версию не вошло. На нашем узле DeepInfer делал всё сразу в одних и тех же восьми движках, а его планировщик выполняет каждый шаг либо целиком как префилл, либо целиком как декод, никогда не смешивая. Ровно под такой сценарий сделан рецепт vLLM, и не под такой — DeepInfer.

07 · СТАРТ15,5 минуты или 88 секунд

8×B300 · от запуска до готовности · одна шкаласекунды
vLLM cold start start and load weights: 184.6 s 185 compile and JIT: 227 s 227 FlashInfer autotune: 319 s 319 CUDA graph capture: 199 s 199 930 s warmctl up processes (CRIU): 48.6 s GPU state: 27.3 s weights and canary: 12.5 s 88.4 s
start and load weights · 185 scompile and JIT · 227 sFlashInfer autotune · 319 sCUDA graph capture · 199 sprocesses (CRIU) · 49 sGPU state · 27 sweights and canary · 12 s

Лог движка показывает, куда уходят 930 секунд холодного старта — с пустыми кэшами на свежем диске. Загрузка весов — малая часть: 96 с вместе с таблицами Engram. Остальное — подготовка GPU: 84 с на torch.compile и настройку KV, 110 с на JIT-прогрев ядер, 319 с на автотюнинг FlashInfer по 21 диапазону токенов и 199 с на захват 67 CUDA-графов. Рецепт vLLM, запущенный нативно накануне на другом узле, поднимался 24 минуты. DeepInfer загружался 298 с с тёплым JIT-кэшем и 748 с с холодным.

warmctl делает медленную часть один раз: собирает движок, прогревает его и снимает целиком — память всех 27 процессов (CRIU), CUDA-состояние каждой GPU (cuda-checkpoint), скомпилированные ядра, результаты автотюнинга и CUDA-графы, а также 372 GiB весов в той раскладке, которую сделал движок, всего 851 GiB. warmctl up восстанавливает его за 88,4 с из page cache: 43 с на процессы, 22 с на состояние GPU, 12 с на перезагрузку весов, затем canary-промпт сверяется с исходным движком, и только потом открывается порт. Узел читал снимок из page cache со скоростью 155 GiB/s. Все ответы, которые мы проверили жадным декодингом после восстановления, были верными.

Снимок есть в каталоге warmctl. На хосте 8×B300 с драйвером NVIDIA 610.57.04:

DeepSeekMIT
DeepSeek-V4.1-Flash-1017

MoE · Engram · DSpark · Oct 17 update · DEP8

  • 8×B300·88 s·851 GiB·610
restore88 s cold start930 s
deepseek-v41-flash-1017Run
$ curl -fsSL https://warmctl.io/install.sh | sh
$ warmctl up deepseek-v41-flash-1017

up выбирает снимок под GPU и драйвер хоста и поднимает OpenAI-совместимый API на порту 8000:

$ curl -s localhost:8000/v1/completions -H 'Content-Type: application/json' -d '{
    "model": "deepseek-v41-flash-1017", "prompt": "The capital of France is", "max_tokens": 8, "temperature": 0}' | jq -r '.choices[0].text'
 Paris.

В профиле — флаги vLLM, размеры графов и сетка прогрева. Эта же модель есть в каталоге для 8×B200 (DEP8, драйвер 615), 4×B200 (TP4) и 8×B300 с драйвером 580 (TP8).

08 · КАКОЙ ДВИЖОК КОГДАКак выбрать между vLLM и DeepInfer

Бенчмарки говорят, насколько быстр каждый движок на одном узле. Что выбрать, зависит от того, что вы строите.

Для большинства команд с одним-двумя узлами ответ — vLLM. За DeepInfer стоит следить как за эталоном того, как DeepSeek запускает свою модель, и его стоит попробовать для детерминированных офлайн-задач. На одном узле, с настройками, которые рекомендует DeepSeek, быстрее не он.