Больше 600K токенов/с: DeepSeek 4.1 Flash на 8×B300, vLLM против DeepInfer
DeepSeek открыла исходники DeepInfer — движка, на котором работает её собственный сервис. Мы сравнили его с vLLM на одном узле 8×B300 с DeepSeek-V4.1-Flash-1017: как модель раскладывается по памяти, на что способен каждый движок на выводе и префилле и почему DeepInfer отстаёт на одном узле.
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, выпущенное без сервиса вокруг него.
| vLLM | DeepInfer | |
|---|---|---|
| Что это | Универсальный 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-кэша, разделение префилла и декода, балансировка |
| Рассчитан на | Любой масштаб, от одной GPU | 4 GPU на префилл и 64 на декод в одном NVLink-домене |
03 · СТЕНД8×B300, одна модель, два движка
| Конфигурация | |
|---|---|
| GPU | 8× 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 строк |
| vLLM | 0.31.1rc1, образ из рецепта vLLM для B300, драйвер NVIDIA 610.57.04; DEP8, 256 последовательностей и шаг 8K токенов на ранг, DSpark с 5 черновыми токенами |
| DeepInfer | deepseek-ai/DeepInfer на 8b16149 (20 октября), собран из исходников с CUDA 13.2; EP8 × DP8, DSpark с 5 черновыми токенами, офлайн API; B300 SXM6 PC, драйвер 580 |
| warmctl | v2.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, чтобы каждый токен попал к своим шести экспертам, а затем возвращают результаты обратно.
Места на каждой GPU остаётся много. Весам одного ранга нужно 46 GiB; профиль резервирует фиксированные 160 GiB под KV-кэш в формате NVFP4 MLA от DeepSeek, и vLLM насчитывает там место для 52 миллионов токенов на ранг — это 264 запроса с полным контекстом 196K. Узел упирается не в память, а в 256 одновременных последовательностей на ранг, 2048 на узел.
05 · vLLM НА ПОЛНОЙ НАГРУЗКЕ658K токенов/с на вход, 65K на выход
Начнём с префилла — главная цифра здесь. Мы отправляли промпты с одним выходным токеном, так что узел только читает их. При 2048 одновременных промптах по 8K рецепт vLLM обрабатывает 658 259 токенов/с, около 82K на GPU; при 512 промптах по 32K — 589 924. Длинные промпты дешевле в пересчёте на токен: промпты по 1K при той же конкурентности дают 462 366.
На декоде 2048 одновременных запросов дают 64 990 выходных токенов/с на коротких промптах и 54 985 на 1K → 1K — около 6900 на GPU при 29 мс на токен. С промптами по 8K вывод падает до 29 500, но параллельно узел читает 235 996 токенов промптов в секунду. 2048 — это и потолок: 256 последовательностей на ранг. При 4096 запросах лишние ждут в очереди, а вывод остаётся на 55 506.
| Вход → выход | Конкурентность | Выход, tok/s | Всего, tok/s | Нативно, выход | TPOT |
|---|---|---|---|---|---|
| 128 → 1K | 2 048 | 64 990 | 73 114 | — | 24.8 ms |
| 1K → 1K | 2 048 | 54 985 | 109 969 | 53 892 | 28.8 ms |
| 1K → 1K | 4 096 | — | — | 55 506 | 32.8 ms |
| 8K → 1K | 2 048 | 29 500 | 265 496 | 29 580 | 52.8 ms |
| 2K → 128 | 2 048 | 19 753 | 335 801 | — | 74.4 ms |
Цифры декода — с движка, восстановленного warmctl; пики префилла — с рецепта, запущенного нативно накануне на B300 SXM6 PC. Там, где мы мерили одну и ту же точку на обоих, расхождение меньше 2%: 54 985 против 53 892 на 1K → 1K, 29 500 против 29 580 на 8K → 1K. Восстановление возвращает движок таким, каким он был, вместе с CUDA-графами и автотюненными ядрами. При вдвое меньшей конкурентности восстановленный движок дал на префилле 554K и 557K.
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 replay | YOCO_PREFILL_TRUNCATION_MODE=compact, 140 токенов | README: настройка по умолчанию для V4.1 |
| DSpark | 5 черновых токенов, проверка по уверенности (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 токенов каждого промпта.
На декоде vLLM впереди везде: в 1,8 раза на коротких промптах, в 2,0 раза на 1K → 1K и в 4,0 раза на 8K → 1K, где каждый запрос сначала префиллит 8K токенов. Лучший результат DeepInfer на 1K → 1K, 27 906 выходных токенов/с, дал прогон по умолчанию; настроенный выдал 21 161 — скорее всего, из-за EPLB, который каждые 30 шагов переставляет экспертов, а случайные токены, и так равномерно распределённые по экспертам, этого не окупают.
На префилле разрыв больше. Настроенный DeepInfer читает 71 632 токенов/с промптов по 8K и 71 875 промптов по 32K, около 9K на GPU; vLLM — 658 259 и 589 924. Bounded replay поднял результат DeepInfer на 32K на 11%, с 64 847, а не вдвое, как можно было ждать по пропущенным слоям, — значит, время уходит в другое место.
| Нагрузка | Конкурентность | vLLM | DeepInfer, настроенный | DeepInfer, по умолчанию | vLLM ÷ лучший |
|---|---|---|---|---|---|
| 128 → 1K | 2 048 | 64 990 | 36 381 | — | 1,8× |
| 1K → 1K | 2 048 | 54 985 | 21 161 | 27 906 | 2,0× |
| 1K → 1K | 4 096 | 55 506 | — | 26 294 | 2,1× |
| 8K → 1K | 2 048 | 29 500 | 7 297 | 6 933 | 4,0× |
| префилл 8K | 2 048 | 658 259 | 71 632 | — | 9,2× |
| префилл 32K | 512 | 589 924 | 71 875 | 64 847 | 8,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 секунд
Лог движка показывает, куда уходят 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:
- 8×B300·88 s·851 GiB·610
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
Бенчмарки говорят, насколько быстр каждый движок на одном узле. Что выбрать, зависит от того, что вы строите.
- API для пользователей на одном или нескольких узлах — vLLM. На этом узле он выдал в 2–4 раза больше выходных токенов и в 8 раз больше на префилле, и он поставляется как сервер с OpenAI-совместимым API. В открытой версии DeepInfer сервера нет вовсе.
- Любая модель, кроме DeepSeek-V4.1-Flash, — vLLM. DeepInfer поддерживает только её.
- Воспроизводимая офлайн-генерация — RL-роллауты, эвалы, синтетические данные — стоит попробовать DeepInfer. Batch-invariant инференс и детерминированный сэмплинг встроены и задокументированы для этой модели, а офлайн-батчи — как раз то, чем такие задачи и пользуются. Сначала измерьте пропускную способность на своих нагрузках: на наших он выдавал от половины до четверти от vLLM на декоде.
- Сервис DeepSeek на NVLink масштаба стойки (GB200 или GB300 NVL72) и команда, которая его поддерживает, — смотрите оба. DeepInfer — это то, как сама DeepSeek обслуживает модель на таком масштабе, но систему KV-кэша, разделение префилла и декода и балансировку придётся строить самим. vLLM закрывает эту часть раздельным префиллом через KV-коннекторы.
- Huawei Ascend 950 — DeepInfer, он поддерживает его наряду с NVIDIA SM100.
- Автоскейлинг, spot-мощности или частые выкатки — какой бы движок ни был, решает холодный старт. Этому нужно 15 минут на узел до первого токена, а рецепт vLLM на другом узле поднимался 24. С vLLM warmctl up возвращает тот же движок за 88 секунд.
Для большинства команд с одним-двумя узлами ответ — vLLM. За DeepInfer стоит следить как за эталоном того, как DeepSeek запускает свою модель, и его стоит попробовать для детерминированных офлайн-задач. На одном узле, с настройками, которые рекомендует DeepSeek, быстрее не он.