Проблема: DSpark против MTP - цифры, которые заставляют задуматься
Запуск DeepSeek-V4-Flash-0731 на llama-server с DSpark-дraft показывает 1-2 токена в секунду. Та же модель на том же железе с MTP-схемой выдаёт стабильные 30-40 токенов/с. Разница в 20-30 раз - не погрешность измерения, а симптом фундаментальной ошибки в конфигурации.
Спекулятивное декодирование по своей конструкции должно ускорять инференс. Draft-модель предсказывает несколько токенов вперёд, основная модель проверяет их за один проход. При высоком acceptance rate получаем прирост скорости без потери качества. Именно это демонстрирует MTP. DSpark в теории способен дать ещё больший буст - но на практике конфигурация, собранная без учёта архитектурных особенностей, превращает ускорение в полную деградацию.
В этом разборе мы пройдём по цепочке причин падения производительности: от неоптимального размещения draft-модели на второстепенном GPU до скрытого оффлоада вычислений в RAM при включённом --fit on. Затем дадим три проверенные конфигурации для разных сценариев - одна мощная GPU, две GPU, ограниченная VRAM - и покажем, в каких случаях проще отказаться от DSpark в пользу MTP.
Материал основан на реальных тестах и конфигурациях, аналогичных тем, что мы разбирали в экспериментах с DSpark в llama.cpp и при оптимизации DeepSeek-V4-Flash на B300.
Как устроены DSpark и MTP в llama-server: быстрый ликбез
MTP (Multi-Token Prediction) - встроенный механизм самой модели. DeepSeek-V4-Flash обучалась предсказывать несколько следующих токенов одновременно, и для этого не требуется отдельная draft-модель. В llama-server MTP включается флагами, специфичными для архитектуры модели, и работает прозрачно: основная модель за один проход генерирует N токенов, проверяет их согласованность и отдаёт клиенту. Потребление VRAM практически не меняется, накладные расходы минимальны.
DSpark - внешняя draft-модель, которая работает параллельно с основной. Она значительно меньше (часто та же архитектура с сильной квантизацией или урезанная версия), генерирует черновик из K токенов, а основная модель проверяет его за один forward-проход. Ключевые параметры в llama-server: --device-draft указывает, на каком GPU размещается draft-модель; spec-draft-n-max задаёт максимальное количество предсказываемых токенов. При правильной настройке DSpark даёт прирост до 2-2.5×, как мы фиксировали в тестах с acceptance rate до 87%.
Принципиальная разница: MTP не создаёт второго потребителя VRAM и не зависит от топологии GPU. DSpark требует дополнительную память под draft-модель и чувствителен к тому, на каком устройстве эта модель находится. Ошибка в размещении - и вместо ускорения получаем 1-2 токена/с.
Диагностика: куда смотреть, когда скорость упала до 1-2 токенов/с
Первые признаки проблемы видны сразу: генерация начинается нормально, но через несколько токенов скорость падает до единиц. Утилизация GPU по nvidia-smi скачет от 90% до 10% с периодом в несколько секунд. В логах llama-server могут появляться предупреждения о нехватке памяти или переносе тензоров между устройствами. CPU при этом загружен на 60-80% - явный признак того, что часть вычислений ушла в оффлоад.
Диагностика начинается с трёх проверок. Первая: смотрим фактическое распределение слоёв. В логах запуска llama-server есть строка с перечислением, сколько слоёв основной модели попало на каждый GPU. Вторая: проверяем размещение draft-модели - параметр --device-draft должен указывать на тот же GPU, где находится основная часть модели. Третья: отслеживаем использование VRAM до и после начала генерации. Если после старта память на основном GPU уходит в ноль, а на второстепенном остаётся свободной - конфигурация разбалансирована.
Проверяем размещение draft-модели: почему CUDA1 может быть не лучшим выбором
Типичная ошибка: основная модель на CUDA0, draft-модель на CUDA1. Логика «разнесём нагрузку» здесь не работает. При спекулятивном декодировании основная модель и draft обмениваются данными на каждом шаге: draft генерирует токены, основная проверяет, результат возвращается для следующей итерации. Если модели на разных GPU, каждый обмен идёт через шину PCIe или NVLink. Для DeepSeek-V4-Flash с её объёмом скрытых состояний эта задержка становится бутылочным горлышком.
Параметр --main-gpu определяет, на каком устройстве выполняются основные вычисления и куда приходят результаты проверки. Если --main-gpu 0, а --device-draft 1, каждый токен проходит путь: CUDA1 (draft генерирует) → шина → CUDA0 (основная проверяет) → шина → CUDA1 (draft получает результат). При генерации 100 токенов с spec-draft-n-max=5 это 20 циклов с четырьмя пересылками каждый. Задержка шины в 10-20 микросекунд накапливается до ощутимых миллисекунд на токен.
Решение: draft-модель всегда размещается на том же GPU, что и основная часть вычислений. --main-gpu 0 --device-draft 0. Если основная модель распределена между GPU, draft остаётся на главном.
Конфликт за VRAM и режим --fit on: скрытая ловушка
Флаг --fit on указывает llama-server автоматически распределить модель по доступной памяти. Алгоритм пытается уместить всё в VRAM, а что не влезает - вытесняет в RAM. Проблема в том, что --fit on ничего не знает про спекулятивное декодирование. Он видит основную модель, видит draft-модель, считает суммарный объём и принимает решение о размещении без учёта того, что draft критически важно держать на GPU.
DeepSeek-V4-Flash-0731 в FP16 занимает около 600 ГБ, с квантизацией IQ3_S - порядка 130 ГБ. Добавляем draft-модель - ещё 10-20 ГБ. Если суммарно доступно 140 ГБ VRAM, --fit on может решить, что draft-модель «лишняя», и вытеснит её в RAM. Инициализация DSpark draft вне GPU приводит к тому, что каждый вызов draft-модели превращается в CPU-вычисление. Скорость падает до 1-2 токенов/с - это чистая производительность CPU на задаче инференса.
MTP эта проблема не затрагивает: нет отдельной модели - нет дополнительного потребителя памяти. Поэтому на тех же 140 ГБ VRAM MTP работает стабильно, а DSpark деградирует.
Решение: отключаем --fit on и задаём --tensor-split вручную. Явно резервируем место под draft-модель на основном GPU. Например, при 48 ГБ VRAM на двух картах: --tensor-split 20,28 оставляет 20 ГБ на CUDA0 под draft и часть основной модели, 28 ГБ на CUDA1 под оставшиеся слои.
Пошаговая настройка llama-server для DSpark: возвращаем 30+ токенов/с
Ниже - три конфигурации, проверенные на связке DeepSeek-V4-Flash-0731 + llama-server. Все примеры используют IQ3_S-квантизацию основной модели и IQ4_NL для draft. Для других квантизаций корректируйте значения --tensor-split под фактический объём.
Вариант 1: одна мощная GPU - всё на CUDA0
Сценарий: один GPU с объёмом VRAM, достаточным для размещения основной и draft-модели. Например, RTX 6000 Ada (48 ГБ) или A100 (80 ГБ).
llama-server \
-m deepseek-v4-flash-0731-iq3_s.gguf \
-md draft-deepseek-v4-iq4_nl.gguf \
--main-gpu 0 \
--device-draft 0 \
--n-gpu-layers 999 \
--ctx-size 32768 \
--spec-draft-n-max 5 \
--host 0.0.0.0 --port 8080
Ключевые моменты: --device-draft 0 совпадает с --main-gpu 0. Все слои на GPU (--n-gpu-layers 999). Никакого оффлоада, никаких пересылок между устройствами. При 48 ГБ VRAM основная модель IQ3_S занимает около 40 ГБ, draft IQ4_NL - около 6 ГБ, остаётся 2 ГБ под KV-кэш. Для контекста 32768 токенов этого достаточно. Скорость: 35-45 токенов/с на RTX 6000 Ada.
Ограничение: если VRAM меньше 48 ГБ, этот вариант не подходит. Переходим к варианту 2 или 3.
Вариант 2: две GPU - правильное разделение
Сценарий: два GPU, например две RTX 4090 (по 24 ГБ) или RTX 3090 + 4090. Суммарно 48 ГБ, но распределённых.
llama-server \
-m deepseek-v4-flash-0731-iq3_s.gguf \
-md draft-deepseek-v4-iq4_nl.gguf \
--main-gpu 0 \
--device-draft 0 \
--tensor-split 18,24 \
--n-gpu-layers 999 \
--ctx-size 32768 \
--spec-draft-n-max 5 \
--host 0.0.0.0 --port 8080
Распределение: 18 ГБ на CUDA0 под draft-модель (6 ГБ) и часть слоёв основной модели (12 ГБ). Оставшиеся 24 ГБ на CUDA1 под основную часть слоёв. Главное - draft остаётся на CUDA0 вместе с --main-gpu 0. Обмен между GPU происходит только для тензоров основной модели при проверке черновика, а не для каждого токена draft-генерации.
Подбор --tensor-split: запустите сервер один раз без draft, посмотрите в логах распределение слоёв. Затем добавьте объём draft-модели к первому значению в split. Для IQ4_NL это около 6 ГБ. Если основная модель в IQ3_S занимает 40 ГБ и равномерно распределяется как 20+20, новый split будет 26,20. Но CUDA1 ограничена 24 ГБ - значит, сдвигаем баланс: 22,18 или 18,24 в зависимости от того, какая карта мощнее.
Ожидаемая скорость: 25-35 токенов/с на двух RTX 4090. Небольшое снижение относительно варианта 1 из-за межкарточных пересылок при проверке черновика, но всё ещё в 20 раз быстрее деградировавшей конфигурации.
Вариант 3: ограниченная VRAM - гибридный подход с --n-cpu-moe
Сценарий: одна карта с 24 ГБ VRAM и много системной RAM (64-128 ГБ). Например, RTX 3090/4090 с DDR5. Основная модель не влезает целиком, но хочется использовать DSpark.
DeepSeek-V4-Flash - MoE-модель. Эксперты занимают значительную часть объёма, но активируются не все одновременно. Флаг --n-cpu-moe выгружает часть экспертов в RAM, оставляя на GPU attention-слои и активных экспертов. Освободившуюся VRAM отдаём под draft-модель.
llama-server \
-m deepseek-v4-flash-0731-iq3_s.gguf \
-md draft-deepseek-v4-iq4_nl.gguf \
--main-gpu 0 \
--device-draft 0 \
--n-gpu-layers 999 \
--n-cpu-moe 39 \
--ctx-size 16384 \
--spec-draft-n-max 3 \
--host 0.0.0.0 --port 8080
Параметр --n-cpu-moe 39 выгружает 39 экспертов на CPU. Это значение подбирается экспериментально: начинаем с 0 и увеличиваем, пока VRAM не освободится достаточно для draft-модели. Каждый эксперт в IQ3_S занимает около 0.5-0.7 ГБ. 39 экспертов освобождают примерно 20-25 ГБ - достаточно, чтобы основная часть модели и draft поместились в 24 ГБ.
Плата за гибридный подход - снижение скорости. Эксперты на CPU считаются медленнее, даже на быстрой DDR5-5600. Пропускная способность системной памяти (~50 ГБ/с) против VRAM (~1000 ГБ/с) даёт о себе знать. Ожидаемая скорость: 8-15 токенов/с. Это значительно меньше 30-40 на чистом GPU, но всё ещё в 5-10 раз быстрее деградировавшего DSpark с оффлоадом.
Дополнительно снижен --spec-draft-n-max до 3. Меньше токенов за цикл - меньше накладных расходов на проверку при ограниченной пропускной способности CPU-части. Контекст тоже урезан до 16384 для экономии VRAM под KV-кэш.
Альтернативы: когда DSpark не нужен, и что делать вместо него
MTP как основной режим: просто и надёжно
Для большинства сценариев MTP - оптимальный выбор. Он не требует отдельной модели, не создаёт конфликтов за VRAM, не чувствителен к топологии GPU. На DeepSeek-V4-Flash-0731 MTP даёт стабильные 30-40 токенов/с на одной RTX 4090 с частичным оффлоадом экспертов - конфигурация, аналогичная нашему тесту на Bosgame M5 с внешней eGPU, где получено 44-59 токенов/с декодинга.
Включение MTP в llama-server зависит от конкретной сборки и модели. Для DeepSeek-V4-Flash поддержка MTP встроена на уровне архитектуры - модель сама активирует многотокенное предсказание, если сервер собран с соответствующими флагами компиляции. Никаких дополнительных параметров командной строки не требуется. Если MTP не активируется автоматически, проверьте, что llama-server скомпилирован с -DLLAMA_CURL=ON и поддержкой актуальной версии API DeepSeek.
Единственный сценарий, где DSpark может дать преимущество перед MTP - очень специфичные задачи с высокой предсказуемостью текста, где acceptance rate внешней draft-модели стабильно выше 85-90%. В таких случаях прирост относительно MTP составляет 10-20%. Но для этого нужна идеально подобранная draft-модель и достаточный запас VRAM.
Гибридные схемы и spec-draft-n-max: компромисс для энтузиастов
Если отказываться от DSpark не хочется, но стабильность MTP привлекательна, работает промежуточный вариант: снижение --spec-draft-n-max до 2-3. Это уменьшает количество токенов, которые draft генерирует за цикл, снижая нагрузку на VRAM и частоту обменов между GPU.
При spec-draft-n-max=5 и проблемной конфигурации каждый цикл спекуляции требует пересылки 5 токенов между draft и основной моделью. При spec-draft-n-max=2 - только 2 токена. Накладные расходы на коммуникацию падают пропорционально. Цена - меньшее потенциальное ускорение: вместо теоретических 2.5× получаем 1.5-1.8×. Но это лучше, чем 0.05× при деградации.
Другой гибридный подход: использовать MTP для основной генерации, а DSpark включать только для специфических участков - например, при генерации кода, где шаблонность повышает acceptance rate. На практике это требует модификации клиентского кода, переключающего режимы на лету, и выходит за рамки стандартной конфигурации llama-server.
Выводы: чек-лист для быстрой настройки спекулятивного декодирования
Проблема «DSpark - 1-2 токена/с, MTP - 30-40» сводится к трём факторам: размещение draft-модели на отдельном GPU, автоматическое вытеснение в RAM при --fit on и отсутствие резервирования VRAM под draft. Каждый фактор исправляется изменением одного-двух параметров.
Чек-лист для диагностики и исправления:
- Проверить размещение draft.
--device-draftдолжен совпадать с--main-gpu. Если основная модель на CUDA0, draft тоже на CUDA0. Никаких исключений. - Отключить
--fit on. Задать--tensor-splitвручную, явно резервируя место под draft-модель на основном GPU. Контролировать распределение по логам запуска. - Убедиться, что draft-модель не уходит в RAM. После старта генерации проверить через
nvidia-smi, что VRAM основного GPU заполнен адекватно (есть запас под KV-кэш, но нет резкого падения utilisation). Высокий CPU usage при генерации - признак оффлоада. - Рассмотреть MTP как основную схему. Для DeepSeek-V4-Flash MTP даёт 30-40 токенов/с без дополнительных моделей и конфликтов за память. DSpark оправдан только при стабильно высоком acceptance rate и достаточном запасе VRAM.
- При нехватке VRAM выгружать экспертов MoE, а не draft. Использовать
--n-cpu-moeдля освобождения памяти под draft-модель. Начинать с малых значений (10-15), увеличивать до достижения стабильной работы. Draft-модель всегда остаётся на GPU.
Конфигурации из этой статьи воспроизводимы на любом стеке с llama-server актуальной сборки. Для более детального погружения в тему спекулятивного декодирования рекомендуем наш разбор DSpark с бенчмарками и анализ NextN/MTP для GLM-5.2 - принципы настройки общие, а цифры помогут сориентироваться в ожидаемом приросте.