Контекст и исходные данные: что мы запускали и что получили
Задача - пакетная обработка коротких текстовых записей с генерацией около 300 выходных токенов на каждый элемент. Оборудование: один ускоритель B300. Ожидалась агрегированная скорость в несколько тысяч токенов в секунду. Реальность оказалась скромнее - 770 токенов/с при размере батча 256.
Конфигурация, с которой проводились замеры:
- vLLM 0.25.0
- FP8 KV-кэш
- block_size=256
- enable_prefix_caching
- cudagraph_mode=FULL_AND_PIECEWISE
Разрыв между ожиданием и фактом заставил копнуть глубже. Всплыли три узких места: отказ специализированного MoE-ядра, деградация от спекулятивного декодирования и вероятный eager-режим для разреженного внимания. Разберём каждое.
Проблема №1: MoE-ядро deep_gemm_mega_moe отказывает на одной GPU
DeepSeek-V4-Flash - модель класса Mixture-of-Experts. Быстрое ядро deep_gemm_mega_moe спроектировано под expert parallel: эксперты распределяются по нескольким ускорителям, коммуникация идёт через NVLink или NVSwitch. На одной GPU этот механизм не работает физически - не с кем распределять экспертов. Ядро просто отказывается запускаться.
Почему expert parallel недоступен на одном ускорителе
Архитектура DeepSeek-V4-Flash предполагает, что часть экспертов живёт на одном устройстве, часть - на другом. При single-GPU инференсе все эксперты вынуждены умещаться в памяти одного B300. Это не только исключает expert parallel, но и создаёт дополнительное давление на пропускную способность VRAM. Каждый токен проходит через маршрутизатор, который выбирает экспертов, и если все они на одном чипе - задержка растёт, throughput падает.
Похожая проблема поднималась в эксперименте с предсказанием загрузки экспертов MoE: основным узким местом в CPU/GPU оффлоуде оказалось ожидание данных через PCIe, а не вычисления. На одном B300 мы упираемся в пропускную способность памяти и невозможность распараллелить экспертов.
flashinfer_trtllm: компромисс или тупик?
После отказа deep_gemm_mega_moe пришлось переключиться на flashinfer_trtllm. Это бэкенд, который работает на одной GPU, но не даёт той же эффективности, что специализированное ядро. Точных цифр сравнения пока нет - deep_gemm_mega_moe просто не запустился, замерять не с чем. Косвенно: 770 токенов/с при батче 256 - это результат работы flashinfer_trtllm. Если бы ядро заработало, скорость была бы ближе к ожидаемым нескольким тысячам.
Для тех, кто ищет альтернативные подходы к инференсу MoE-моделей на ограниченном железе, полезен разбор стриминг-инференса через TensorRT-LLM и DeepSpeed. Там детально рассмотрены сценарии, когда памяти не хватает даже на полную загрузку модели.
Проблема №2: спекулятивное декодирование DSpark снижает пропускную способность
DSpark - метод спекулятивного декодирования, который предсказывает несколько следующих токенов параллельно, а затем проверяет их корректность. На малых батчах и при генерации структурированного контента это даёт ускорение. Но в насыщенном батче из 256 параллельных запросов механика ломается.
Когда спекулятивное декодирование перестаёт помогать
При большом batch size GPU и так загружена вычислениями под завязку. DSpark добавляет накладные расходы: нужно генерировать черновые токены, проверять их через основную модель, отбрасывать несовпавшие. В насыщенном режиме эти дополнительные шаги не окупаются - вычислительные ресурсы отнимаются у основного инференса. Результат: пропускная способность падает ниже baseline без спекулятивного декодирования.
Это согласуется с тестированием DFlash и MTP для Qwen3.6-27B: DFlash эффективен в генерации JSON и структурированных данных, но в творческих задачах и при высокой нагрузке его эффективность опускается ниже базового уровня. Для пакетной обработки коротких текстов с батчем 256 DSpark оказался контрпродуктивен. Решение - отключить спекулятивное декодирование и перейти на стандартный autoregressive режим.
Проблема №3: разреженное внимание V4 в eager-режиме - где cuda graphs?
DeepSeek-V4-Flash использует sparse Multi-head Latent Attention (MLA) - разреженный механизм внимания, который экономит память и вычисления. В конфигурации указан cudagraph_mode=FULL_AND_PIECEWISE, но есть подозрение, что для sparse MLA этот флаг не срабатывает и внимание работает в eager-режиме. Без cuda graphs каждый вызов ядра внимания проходит через полный цикл launch overhead, что на коротких последовательностях съедает значительную долю времени.
Как проверить, используется ли cuda graphs для sparse MLA
Прямой метод - профилирование. Запустите инференс под nsys или ncu и посмотрите на паттерн вызовов CUDA-ядер. Если для операций внимания видны многократные запуски мелких ядер вместо одного захваченного графа - cuda graphs не применяются. Логи vLLM с уровнем DEBUG также покажут предупреждения о невозможности захвата графа для определённых операций.
Косвенный признак: при включении cudagraph_mode=FULL_AND_PIECEWISE не наблюдается значительного прироста производительности по сравнению с режимом без cuda graphs. Если sparse MLA действительно в eager-режиме, это объясняет часть потерь относительно ожидаемой скорости.
Эксперименты с флагами: что пробовали и что получилось
Перебраны комбинации cudagraph_mode: FULL, PIECEWISE, FULL_AND_PIECEWISE. Ни одна не дала убедительного ускорения для sparse MLA. Попытки кастомизации бэкенда внимания через параметры vLLM тоже не привели к включению cuda graphs для разреженного пути. Результат отрицательный, но это важный сигнал для сообщества: текущая версия vLLM 0.25.0, вероятно, не поддерживает захват графов для sparse MLA DeepSeek-V4-Flash.
Для сравнения: в архитектуре openPangu-2.0-Flash MLA-кэш сокращает память в 4-8 раз, и там cuda graphs работают штатно. Разница в реализации sparse-пути DeepSeek требует отдельных флагов или патчей, которых пока нет в mainline vLLM.
Вопросы к сообществу: что мы хотим узнать
На текущем этапе сформулированы конкретные запросы к тем, кто уже запускал DeepSeek-V4-Flash на одном B300 или аналогичном ускорителе:
- Реальные цифры tok/s. Кто-нибудь получал больше 770 токенов/с на одном B300 при батче 256 и коротких последовательностях? Какая конфигурация использовалась?
- MoE-бэкенды для одной GPU. Существуют ли альтернативы flashinfer_trtllm, которые работают без expert parallel и дают лучшую производительность? Пробовал ли кто-то адаптировать deep_gemm_mega_moe под single-GPU через эмуляцию expert parallel?
- Флаги для cuda graphs sparse MLA. Есть ли недокументированные параметры vLLM или переменные окружения, которые принудительно включают захват графов для разреженного внимания DeepSeek-V4-Flash? Может, нужен патч ядра?
- Альтернативные конфигурации vLLM. Какие значения block_size, max_num_seqs, gpu_memory_utilization дают лучший throughput на одном B300 для этой модели?
- Квантизация. Стоит ли переходить на IQ3_XXS-AS или IQ2_S для B300, как это делают на RTX 3090? Насколько это повлияет на точность в пакетной обработке?
Предварительные выводы и дальнейшие шаги
Что известно точно: 770 токенов/с на одном B300 - это не предел. Основные потери связаны с тремя факторами: невозможность использовать expert parallel для deep_gemm_mega_moe, деградация от DSpark в насыщенном батче и вероятный eager-режим sparse MLA. Отключение спекулятивного декодирования уже даёт прирост. Переход на flashinfer_trtllm - вынужденная, но рабочая мера.
Гипотезы для проверки:
- Сборка vLLM из исходников с патчем для принудительного захвата графов sparse MLA.
- Тестирование квантизаций IQ3_XXS-AS и IQ2_S на B300 - на RTX 3090 разница между ними в скорости генерации составила 18-19%, на B300 соотношение может быть иным.
- Сравнение с инференсом через кастомные рантаймы вроде Eider для DGX Spark - нативная поддержка NVFP4 и экспертная свопинг-память могут дать другой профиль производительности.
Приглашаем к обсуждению и совместным экспериментам. Если у вас есть результаты запуска DeepSeek-V4-Flash на одном B300 или вы нашли способ включить cuda graphs для sparse MLA - поделитесь в комментариях. Конкретные цифры, конфиги и патчи сейчас важнее общих рассуждений.