[Spec Decode] Add top-k DSpark Markov projection - #49969
Conversation
|
Documentation preview: https://vllm--49969.org.readthedocs.build/en/49969/ |
|
This pull request has merge conflicts that must be resolved before it can be |
ebd8257 to
39c1628
Compare
Signed-off-by: Andrii Skliar <askliar@nvidia.com>
39c1628 to
6bc2306
Compare
benchislett
left a comment
There was a problem hiding this comment.
High level: I feel like this approach may be overly optimized.
I'm not convinced that sampling from a smaller distribution will bring efficiency here. If the savings are just coming from the sparse markov projections, then we could probably gain a lot just by having the bias module directly overwrite the logits of the backbone (with within-topk being written as calculated and outside-topk set to -inf or 0). That would eliminate the need for custom sampling code
| that dense buffer so the normal sampler sees the truncated proposal. | ||
| """ | ||
| weight = self.markov_w2.weight[index] | ||
| bias = torch.bmm(weight, markov_embed.unsqueeze(-1)).squeeze(-1) |
There was a problem hiding this comment.
Is it possible to use addmm here?
See #50737
If it requires a layout change or something heavy, don't bother. But if it happens to be a drop-in, seems like a nice potential improvement
There was a problem hiding this comment.
Replaced with baddbmm - that should be the easiest drop-in replacement.
| self.draft_model_config.hf_config, | ||
| ) | ||
| ) | ||
| if ( |
There was a problem hiding this comment.
This constraint is no longer required. Please rebase to latest main, which includes a change to replicate the markov_w2 on all gpus (TP disabled)
Signed-off-by: Andrii Skliar <askliar@nvidia.com>
Signed-off-by: Andrii Skliar <askliar@nvidia.com>
Signed-off-by: Andrii Skliar <askliar@nvidia.com>
b8dd547 to
3c9ea0c
Compare
Signed-off-by: Andrii Skliar <askliar@nvidia.com>
Signed-off-by: Andrii Skliar <andreyws96@gmail.com>
Every cold boot on the nvidia DSpark path under the V2 runner dies in profile_run: DSparkSpeculator._sample_logits calls model.map_draft_to_target on the draft_logits-is-None branch, which is exactly the state during profiling. vllm-project#49969 added the hooks to amd/, xpu/, kimi_k3 and qwen3_dspark but not this class. Identity is correct and checked, not assumed: nothing in-tree assigns _d2t_scatter_index, 0731 sets no dspark_draft_topk, and head/logits_processor/markov_w2 are all built at config.vocab_size. compute_draft_logits mirrors amd/xpu. Reported by alexbi29 in vllm-project#41834 with the exact fix, verified on their SM120 TP=2+EP production cluster. Co-authored-by: alexbi29 <alexbi29@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
Purpose
DSpark computes the base logits for all draft positions in parallel, then applies the Markov bias sequentially over the full draft vocabulary. The repeated full-vocabulary Markov projection is on the serial drafting path.
This PR adds
dspark_draft_topkfor Qwen3 DSpark. It selects the top-k candidates from the base logits once, gathers the correspondingmarkov_w2rows, and evaluates the sequential Markov bias only for those candidates.The current implementation deliberately reuses the existing dense sampling path:
-inf.The base LM-head output and probabilistic draft-logits storage remain dense. This optimization specifically reduces the repeated sequential Markov projection while avoiding custom proposal and rejection-sampling plumbing.
Current scope:
Test Plan
dspark_draft_topkacross lower- and higher-concurrency workloads.Results
End-to-end comparison
Tested with
draft_sample_method: probabilistic, temperature 1, and 7 speculative tokens on GB10.¹ Concurrency 64, ISL 32768 / OSL 1024.
Decode GPU profile
GPU time by selected kernel category, in milliseconds, over a concurrency-1 / approximately 8K-context decode window:
cutlass/ GEMV)The profiles confirm that the saving occurs where expected:
The concurrency-1 profile establishes the per-step compute saving. At concurrency 64 with a 32K-token input, decode becomes GPU-bound and that saving converts into substantial throughput. The current path's advantage over the initial path at high concurrency is consistent with its lower auxiliary kernel/index overhead, although that contribution was not isolated independently.
Acceptance-length sweep
Focused checks
pre-commit run --files <changed files>: passed, including Ruff and mypy.pytest -q tests/v1/spec_decode/test_dspark_topk.py: 2 passed.pytest -q tests/test_config.py -k 'draft_sample_method': 2 passed.pytest -q tests/v1/worker/test_gpu_rejection_sampler_chunking.py -k 'preserves_request_boundaries': 1 passed.Benchmark caveats
gpu-memory-utilization=0.8.gpu-memory-utilization=0.7because CUPTI trace buffers caused model-load OOM at 0.8 on this node. The decode kernels were unchanged, so the profiles remain representative of the code paths.Essential Elements of an Effective PR Description Checklist