Repository navigation
Conversation
…edy draft The DSpark greedy drafter selects a token per speculative step with a full-vocabulary torch.argmax. At the shapes the drafter runs at (a few rows, 129280 columns) that kernel is launch- and latency-bound, costing ~43 us per call regardless of the row count, and the five steps are dependency-chained so the cost lands five times on every decode step. Replace it with a two-stage split-vocabulary reduction: per-block partial argmax, then a small final reduction. The comparator defines a total order (NaN highest, then value, then lowest index), so the result is independent of the reduction order and bit-exact with torch.argmax, including first-index ties and NaN propagation. Unsupported inputs fall back to torch.argmax. GB200, BF16, vocabulary=129280, CUDA Graph + CUPTI, cold L2: 43.4 -> 4.4 us at M=1 and 44.4 -> 8.0 us at M=32. Signed-off-by: Juntian Liu <Juntianl777@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drop the CUDA Graph replay and fallback cases, and add random rows plus a row whose first maximum lies after the first block with ties in later blocks, so the cross-block first-index tie-break is exercised. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Juntian Liu <Juntianl777@gmail.com>
Add an opt-in decode executor for small decode batches of
DeepSeek-V4.1-Flash on GB300 (TP4, DSpark), enabled with
--additional-config '{"decode_backend": "latency_specialized"}' (the
default, auto, keeps the stock decode), under
vllm/models/deepseek_v41/nvidia/specialized_decode/. As in vllm-project#61867, the
model owns the executor: the model state builds it once the weights are
loaded, and FULL-graph captures of pure decode steps run their decoder
layers on it. Its paths:
- a fused Lamport all-reduce + mHC at every sublayer boundary up to 384
rows, writing the attention input as MXFP8, collecting wo_b's
published output and taking the deferred MoE finalizes;
- GEMMs for 1-8 rows: a CuTe DSL routed-expert front, route kernel and
tcgen05 GEMMs; FlashInfer's low-latency MXFP8 GEMM with pinned
tactics for the attention projections; a low-batch BF16 GEMM for the
indexer projections; a CuTe DSL LM head and the custom all-gather for
logits;
- indexer and attention: shared sparse-index tables, a persistent KV
counter for the sparse-MLA decode, cooperative top-k, side-stream
indexer work and a row-sharded dense indexer;
- attention-metadata builds replayed from CUDA graphs, and the Engram KV
projection chain on the prefetch stream.
The executor is validated on GB300, MRV2, TP4 and DSpark; any other
deployment raises at start-up on every TP rank with the requirements it
misses. With the default, the model runs as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com>
- Rename specialized_decode/README.md to AGENTS.md, like the other package guides. - Explain why the eligibility check takes a MIN all-reduce over the TP group: NVLink multicast is probed on each rank, and ranks that disagree would hang. In AGENTS.md and at the all-reduce. - Drop test_decode.py, whose tests restate the code, and its entries in the CPU CI step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com>
- vLLM main now pins flashinfer-python 0.7.2. The FlashInfer internals that the publishing wo_b GEMM, the MXFP8 layer input and the pinned MXFP8 tactics use are unchanged in 0.7.2, as is the sparse-MLA decode's KV-counter argument, so the four features run on 0.7.2 instead of falling back to the stock path. - LLBf16Gemm.dispatch now takes the output dtype (vllm-project#62042); the attention side projections' AuxProjectionGemm, which overrides it, passes it through. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com>
With decode_backend latency_specialized, the DSpark draft of DeepSeek-V4.1-Flash gets a decode executor of its own, which the target's model state builds on the target executor's kernels and streams. The speculator's per-step work is fused, captured in a CUDA graph or moved off the critical path: - the draft layers on the fused all-reduce + mHC, the executor's attention and the routed-expert and MXFP8 GEMMs, with a CuTe DSL draft MoE finalize; - the Markov loop as one sampling kernel per draft position, and the whole chain in one cooperative CuTe DSL kernel per rank with the Markov head sharded by vocabulary; - sampling, postprocess_sampled and the draft input preparation in a post-forward CUDA graph, which a model state offers through post_forward_cudagraphs, with the draft context work on a side stream; - all draft KV-cache groups prepared in one launch. This applies to every DFlash, DFlash2, LiLiCorr and DSpark user and writes the same outputs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com>
Resolve the conflict in .buildkite/test_areas/models_basic.yaml: keep the DeepSeek-V4.1 B200 step and main's new Kimi-K3 MI355 step. Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…latency-decode Follow vllm-project#61868, which merged vLLM main. Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow main after vllm-project#61868 landed. The specialized decode files take this branch's versions (main's equal the vllm-project#61868 revision this branch carries); .buildkite keeps both main's new test and this branch's dspark source dependency. Signed-off-by: Yaoyao Ding <yaoyaod@nvidia.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto main 51d2a85 Signed-off-by: mgoin <mike.goin12@gmail.com>
…elSpeculator Address review: both reduction stages use tl.argmax with NaN mapped to -inf (as in the rejection sampler), and the generic DraftModelSpeculator greedy path uses greedy_argmax too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
Signed-off-by: mgoin <mike.goin12@gmail.com>
…n SM90 Enable the mHC coefficient overlap on Hopper when the fused Lamport all-reduce + mHC kernel applies (TP4, NVLink multicast). On H200 TP4 the fused path wins through 144 tokens, against 16 on GB200, so the token cap becomes a per-architecture mhc_overlap_max_tokens(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mike.goin12@gmail.com>
… TP4) Run decode_backend=latency_specialized on SM90: HopperSpecializedDecode reuses the executor's plan, layer loop, boundaries and side streams over the stock FlashMLA layers, for FULL-graph decode steps of up to 192 rows (32 DSpark requests), with Hopper kernels for the rows they serve: - dense FP8 linears on a low-batch CuTe DSL GEMM (X multicast in clusters at 6-16 rows) or tuned Humming, wo_b publishing into the fused all-reduce + mHC mailbox; - a persistent low-batch MoE kernel on the stock Humming W4A8 experts (routing, both expert GEMMs and the shared expert), finalized by the next boundary, up to 144 rows; - sparse MLA sharing FlashMLA plans and top-k tables across layers, and small-batch MLA kernels with early-trigger q/kv norm, query RoPE and combine + inverse RoPE up to 12 rows; - a fused FP8 indexer (logits + candidate bitmap, then the stock cooperative top-k) up to 48 rows; - the DSpark draft on HopperDraftDecode. Boundaries above the fused all-reduce + mHC cap take the stock all-reduce and mHC. GB300 behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mike.goin12@gmail.com>
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
Follow main in the stacked DSpark and Hopper code: - read the draft logits through DraftLogitsCache, and keep the fused Markov sampler to the replicated cache it indexes by request state; - import the CuTe DSL primitives from vllm.models.common.ops.moe_tail_fusion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
2 of 6 tasks
This branch has not been deployed
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.
Overview
Add a Hopper (SM90) backend to DeepSeek-V4.1's specialized decode executor (#61868), for DeepSeek-V4.1-Flash at TP4 on H200 with DSpark k=5. It is opt-in, as on GB300, through
--additional-config '{"decode_backend":"latency_specialized"}'. Decode steps of up to 192 rows run in the executor; larger steps, prefill and anything it does not serve fall back to the stock path over the same weights.Stacked on #61869 (DSpark low-latency decode), #57770 (split-vocabulary greedy argmax) and #64892 (SM90 mHC overlap behind the fused all-reduce). Pure TP4 with Humming MoE also needs humming#177 (humming-kernels 0.1.19, #64802).
Reviewing: the Hopper executor commit is the new work, plus the merge of main on top of it, which adapts the stack to main's
DraftLogitsCacheand the moved CuTe DSL primitives. Everything before them comes from #61869 and #64892. #57770 is merged in main. #61869 is still open, so its changes are in this diff until it lands.Duplicate check: there is no other open Hopper backend for the specialized decode. #62958 and #64442 only touch DSpark sampling.
Claims
VLLM_GPU_SYNC_CHECK=error, re-run on the merged build: 0 at c1, c8 and c32).model_runner.pychanges in this diff come from [Perf][DSv4.1] DSpark draft on the specialized decode #61869. Everything in the Hopper commit is undervllm/models/deepseek_v41/nvidia/specialized_decode/, and SM100 behavior is unchanged.Validation
Setup: 4× H200,
--tensor-parallel-size 4 --max-model-len 200000 --max-num-seqs 64, DSpark k=5, Humming MoE with humming#177. "Off" is the same build with the executor disabled, so it includes every stacked PR. Runs are in ABBA order (on, off, off, on).Speed, real prompts, output length 1024, temperature 0.7. Median ITL in ms:
Acceptance lengths match between arms (2.79–3.15).
Long context, 64K prompts, two on/off pairs. Median ITL in ms:
Quality.
The concurrency sweep and the 8-repeat evals ran before a fused-indexer fix at long context. On the final build c1 and c8 measure 5.39 and 14.40 ms, unchanged.
After merging main (
f1df4a3ae9). Median ITL in ms, on a new 160-prompt set fromRedHatAI/speculator_benchmarks:Only one on run per point is clean. The tables above this one are from before the merge and used an earlier prompt set, so compare numbers only within a table. The c8 gain is smaller here than the −13.5% in the sweep above. The cause isn't established: main, the prompt set and acceptance length (3.25 on, 3.18–3.50 off) all changed, and the runs are few. A clean c1–c32 sweep on the merged build is still to do; the attempt so far overlapped other work on the GPUs and was discarded.
Tests.
pytest tests/models/deepseek_v41/nvidia/specialized_decode/test_hopper.pyon H200: 19 passed.test_hopperis wired into Buildkite (models_basic.yaml).Reproduce:
The executor-on arm adds
--additional-config '{"decode_backend":"latency_specialized"}'tovllm serve. The executor-off arm is the default.Details
Where the time goes. At c1 the stock step is mostly fixed per-kernel cost, not bandwidth:
So the executor:
The MoE runs near its streaming limit (about 4 TB/s).
Compatibility. The kernels read the weights in stock's layouts:
So steps the executor doesn't serve fall back to stock with one copy of the weights. The exception is an FP8 copy of
wo_a, because stock SM90 keeps it only as BF16. It costs about 0.36 GB per GPU (1.44 GB in FP8 across 43 layers, sharded TP4).Size. About 10.8K lines under
hopper/, mostly kernels:Plus +342/−85 in the shared specialized-decode package, which lets the executor select its backend by architecture and records metadata builds for the Hopper builders. Plus 1.0K lines of tests.
It mirrors the GB300 backend's structure, so the two share the executor runtime, step schedule and fallbacks.
Limitations.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code