DSpark на двух DGX Spark: порт, баг на одну строку и бенчмарки с глубиной до 1M
DeepSeek выпустил DSpark - спекулятивный декодер для V4. Публичного пути для GB10 в окне 27-30 июня не было, команда портировала сама, нашла унаследованный баг (один сдвиг индекса), поднявший acceptance с 0.41 до 0.74. Агрегат вырос с 64 до ~99 tok/s на 8 потоках. Плюс три задокументированных негативных результата.
DeepSeek выпустил DSpark 27 июня 2026 - спекулятивный декодер поверх чекпойнта V4, часть их стека DeepSpec под MIT-лицензией. Команда с двумя DGX Spark (GB10, SM121) не нашла рабочего пути для своей конфигурации и решила не ждать.
Что такое DSpark и почему пришлось портировать
Спекулятивный декодинг работает так: маленькая быстрая «драфт»-голова набрасывает несколько токенов вперёд, большая модель проверяет их одним широким проходом и принимает совпавшие. Доля принятых токенов (acceptance) и определяет выигрыш.
DSpark - это три MTP-стадии под namespace mtp.*, каждая структурно равна обычному слою V4 (MLA-attention + MoE на 256 экспертов). Сверху обвязка: main_proj, принимающий конкатенацию hidden-состояний трёх слоёв таргета ([40,41,42]), low-rank Markov-голова (rank 256) и confidence-голова. За шаг драфт набрасывает блок из 5 токенов, таргет проверяет их одним проходом шириной 6 токенов.
В окне 27-30 июня 2026 в публичном vLLM-пути для GB10/SM121 method=dspark не был рабочей опцией: в форке это закрытый pydantic-enum, а не флаг. ValidationError прилетал раньше, чем успевали скачаться 165 ГБ весов. Команда строила поверх community-форка jasl (PR #41834 в upstream vLLM) - на нём и портировала.
Оговорка по датам: экосистема двигалась быстро, и уже после выхода статьи карточка модели на HF показывает штатный vLLM-запуск со --speculative-config '{"method":"dspark",...}'. Все утверждения про upstream/vLLM относятся именно к тому окну.
Баг в одну строку, убивавший качество драфта
Порт лёг двумя новыми модулями (DSparkV4MTP и DSparkProposer, унаследованный от DFlashProposer) и правками на семь строк: enum, предикаты, диспетчер раннера. Загрузилось, поехало - и выдало мусор: acceptance позиции 0 около 5%, драфт предсказывал почти шум.
После охоты за багом нашли три фикса.
Два «нормальных»: aux hidden-состояния таргета нужно сворачивать per-layer (hc-fold - проекция hc_post плюс усреднение по оси вспомогательных hidden-состояний), а не отдавать сырым тензором; финальную норму чекпойнта (mtp.N.norm.weight) надо применять перед LM-головой, и токены блока должны видеть друг друга non-causally (унаследованный билдер делал блок каузальным).
Главный баг - один: унаследованный кернел сэмплил драфт-токены из noise-слота вместо bonus-слота. Сдвиг индекса сэмплинга на один слот дал:
- acceptance позиции 0: 0.41 → 0.74
- средняя принятая длина: 1.75 → 2.5
- throughput: 26 → 36 tok/s
Цифры стенда: что реально получилось
Отправная точка для сравнения - продовая нагрузка из части 5: MTP-2 давал ~40 tok/s single-stream и ~64 tok/s агрегата на 8 потоках.
С DSpark на том же стенде (2x GB10, TP=2 через QSFP 200G / RoCEv2):
- single-stream fp8: 26.7 tok/s без спекуляции → 32.4 tok/s с DSpark (+21-24%)
- агрегат на 8 потоках: ~99 tok/s против ~64 у прежнего MTP-2 прода (+55%)
- агрегат на 12 потоках: 141-146 tok/s
Это не опровержение заявленных DeepSeek цифр (+60-85% per-user speedup и кратно большие aggregate-выигрыши в SLA-режимах). Те числа сняты над baseline MTP-1 и на другом железе - команда сравнивает другую метрику с другим baseline.
NVFP4: механизм выигрыша оказался неочевидным
После публикации комьюнити пользователь tonyd2wild выложил NVFP4-KV рецепт с заявкой 67 tok/s single-stream против 32 у команды. Воспроизвели.
Результат: single 55-63 tok/s, C16-static 324 против его 315.1 - ничья в пределах разброса.
Важный момент про механизм: в no-spec контроле 4-битный KV не ускорил голый forward (26.6 ≈ 26.7 tok/s у fp8). Single-stream выигрыш рецепта пришёл через acceptance 3-4 против ~2.1 у fp8-варианта. То есть NVFP4 помогает не через ускорение вычислений, а через то, что драфт чаще угадывает.
Деградация до 1M и петли когерентности
Данных по поведению на реальных длинных контекстах в паблике не нашли - сняли сами.
Кривая deградации throughput:
- 16K: 42.7 tok/s
- 512K: 17.6 tok/s
- 1.03M: 17.2 tok/s
После 512K - полка, не обвал. Это важно: модель не деградирует бесконтрольно на сверхдлинных контекстах.
Но есть другая проблема: на глубине ≥256K билд иногда уходит в петли (repetition loops). На 1M оба прогона оказались связными - это лотерея, а не гарантия. И acceptance в петле завышается, что искажает метрику.
Три негативных результата
Это, пожалуй, самое ценное в материале - задокументированные тупики:
- YaRN re-anchor драфта - замедление в ~15 раз, видимый «рост» acceptance оказался миражом.
- Confidence-scheduler - минус 9% агрегата, не плюс.
- Сэмплер-фикс когерентности - ни один конфиг не держит обе глубины,
min_pпод spec-decode заблокирован сервером.
Что это значит
Если у тебя стенд на GB10 и ты хочешь запустить DSpark - форк и данные лежат в репозитории botAGI/DeepSeek-V4-Flash-DSpark-GB10-2x-DGX-Spark-1m-fp4-fp8.
Главный вывод из материала: однострочный баг с индексом слота может убить половину потенциального выигрыша спекулятивного декодинга - acceptance 0.41 против 0.74 это не мелочь. Если воспроизводишь чужой рецепт и цифры не сходятся, стоит смотреть в кернел сэмплинга.
По NVFP4: если видишь большой single-stream выигрыш от 4-битного KV, проверь acceptance, а не только latency forward-прохода - выигрыш может быть совсем не там, где кажется.
Источник: Habr, 5 июля 2026 - читать статью