[atom-vllm]: enable DSpark speculative decoding for DeepSeek-V4-Flash-0731 - #2137
Open
peizhang56 wants to merge 1 commit into
Open
[atom-vllm]: enable DSpark speculative decoding for DeepSeek-V4-Flash-0731#2137peizhang56 wants to merge 1 commit into
peizhang56 wants to merge 1 commit into
Conversation
Adds DSpark (`--speculative-config '{"method":"dspark"}'`) as a draft method for
DeepSeek-V4 in vLLM plugin mode, where ATOM owns the model and vLLM owns the
scheduler and KV cache manager.
Model
- `DeepseekV4Model.forward` optionally returns per-layer auxiliary hidden states
for EAGLE3/DSpark/DFlash drafts, averaged over the mHC dimension to match the
target the drafter was trained against. `DeepseekV4ForCausalLM` PCP-all-gathers
each aux tensor outside the compile boundary.
- The aux branch sits inside the `@support_torch_compile` region, so
`aux_hidden_state_layers` is baked into the graph. From `--level 2` up the
custom dispatcher replays code object 0 without evaluating guards, so it must
be set at load time and never mutated after; noted at the top of the file.
KV cache
- DSpark's draft needs a second KV group (sliding-window MLA, block 64) beside
the V4 proxy's block 128. `dspark_draft_kv_patch` registers the spec lazily and
exposes it for pickle via PEP-562 `__getattr__`.
- Prefix caching: V4's SWA ring is not carried by vLLM's block cache, so every
hit is rolled back by `max(win_with_spec, index_topk)` tokens and the tail
re-forwarded. Converted per group, since the two groups differ in block size.
The `index_topk` term is an empirical workaround for a sparse-indexer defect
that also reproduces with prefix caching off; flagged as a KNOWN ISSUE.
Correctness fixes found along the way
- `VocabParallelEmbedding.forward` uses `replicated_embedding` instead of
`F.embedding` on the `tp_size == 1` branch. `replicated_embedding` already
masks the -1 spec placeholder async scheduling can emit, and its comment
states VocabParallelEmbedding routes through the masked op -- true only of
the `tp_size > 1` branch. Single-GPU still reached a raw gather, which reads
the row before the table and GPU-faults.
- Async scheduling removes the sampled-token sync, so the pinned staging buffers
can tear. Gated with a depth-1 `torch.cuda.Event`.
- Idle DP ranks reach `build()` via `execute_dummy_batch()` with
`seq_len == query_len`; synthesized a decode context for that case.
- Piecewise cudagraph modes are demoted on the plugin path, a demotion vLLM
skips when it compiles nothing.
Request ids are passed to ATOM metadata builders through a thread-local instead
of a per-step `block_table[:, 0]` D2H copy, covering both of vLLM's unrelated
`GPUModelRunner` classes (V2 is forced for dspark).
Tests: 4 new/extended vLLM plugin test files; `tests/plugin` 201 passed. The 34
sglang failures are pre-existing and unrelated. black + ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
yitingw1
requested review from
ZhangLirong-amd
and removed request for
yitingw1
September 7, 2026 06:58
|
|
||
| # 1-based layer ids whose residual is also returned, for EAGLE3 / | ||
| # DSpark / DFlash drafts. Set via `set_aux_hidden_state_layers`. | ||
| self.aux_hidden_state_layers: tuple[int, ...] = () |
Collaborator
There was a problem hiding this comment.
can we do this in plugin bridge? Since aux_hidden_state we configure it in model_runner in ATOM native, not in model.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Closes #2136.
DeepSeek-V4-Flash-0731 runs with dspark in ATOM native mode but not through the vLLM plugin. This PR brings the plugin to parity: DSpark as a draft method via
--speculative-config '{"method":"dspark"}', plus four correctness fixes on the existing V4 plugin path that block the model regardless of spec decode.Technical Details
Model.
DeepseekV4Model.forwardoptionally returns per-layer aux hidden states for EAGLE3/DSpark/DFlash drafts, averaged over the mHC dim;DeepseekV4ForCausalLMPCP-all-gathers them outside the compile boundary. The aux branch sits inside@support_torch_compile, soaux_hidden_state_layersis baked into the traced graph and must be set at load time: from--level 2up, the dispatcher replays code object 0 without evaluating guards, so later mutation is silently ignored. Noted in a COMPILE BOUNDARY comment inatom/models/deepseek_v4.py.KV cache. The draft needs a second KV group (sliding-window MLA, block 64) beside the V4 proxy's block 128;
dspark_draft_kv_patchregisters it lazily and exposes it for pickle via PEP-562__getattr__. V4's SWA ring is not keyed by a vLLM block, so a prefix-cache hit reads a stale window; every hit is rolled back bymax(win_with_spec, index_topk)tokens and the tail re-forwarded, converted per group since the block sizes differ.KNOWN ISSUE: the
index_topkterm is empirical (512 on V4-Flash vs a 128-token window), and the same floor reproduces with prefix caching off, so the real defect is in the sparse indexer. This rollback is a workaround; happy to track it separately.Correctness fixes (independent of DSpark, latent on the existing V4 path):
VocabParallelEmbedding.forwardused a rawF.embeddingon thetp_size == 1branch, which GPU-faults on the-1spec placeholder async scheduling can emit. Nowreplicated_embedding, which already masks it; its comment says VocabParallelEmbedding routes through the masked op, true only of the TP>1 branch.torch.cuda.Event.build()viaexecute_dummy_batch()withseq_len == query_len; synthesized a decode context.Perf. Request ids reach ATOM metadata builders through a thread-local instead of a per-step
block_table[:, 0]D2H copy.Test Plan
New:
test_vllm_dspark_draft_kv_patch.py,test_vllm_v4_async_scheduling_guards.py. Extended:test_vllm_026_compat.py,test_vllm_kimi_k3.py.Run on this branch (
6fef3b9d) and its base (412f5bfe) to separate regressions from pre-existing failures. Accuracy: DeepSeek-V4-Flash-0731, 8x MI355X (gfx950), TP8, GSM8K 5-shot, 200 questions, vialm_evallocal-completions against a live server, with a spec-off control and a third arm attemperature 1.0to exercise the stochastic rejection path.Test Result
All 36 are pre-existing and unrelated (27 sglang, 9 rtpllm), failing identically on base.
black --check .clean; ruff on the touched files is 24 findings vs 33 at base.GSM8K exact_match (strict):
DSpark costs 0.005 against the control, inside one stderr, and both greedy arms clear the 0.94 bar the nightly
DeepSeek-V4-Pro TP8case uses. The temperature arm differs from greedy on 197/200 questions, confirming the stochastic verifier is engaged: a draft that ignored the requested distribution would return the greedy text and score identically.Prefix-cache sweep on V4-Flash-0731: rollback 128/256/384 -> 1/6 correct, 512 -> 6/6, and 512 holds at 2K/6K/17K prefixes.
Not run: throughput benchmarks.
.github/workflows/atom-vllm-test.yamlis gated behindci:fullor an approving review, so it needs a maintainer to trigger. No CI case passes--speculative-configtoday, so none exercises dspark; glad to add one.Submission Checklist