Repository navigation
[Spec Decode][Sampling] Batch-sharded draft logits - #63554
Merged
WoosukKwon merged 3 commits intoOct 26, 2026
Merged
Conversation
TheEpicDolphin
force-pushed
the
batch-sharded-draft-logits
branch
from
October 26, 2026 20:03
85c87c5 to
c6dd5ac
Compare
TheEpicDolphin
force-pushed
the
batch-sharded-draft-logits
branch
from
October 26, 2026 20:20
c6dd5ac to
93eac5c
Compare
Signed-off-by: Giancarlo Delfin <gdelfin@inferact.ai>
TheEpicDolphin
force-pushed
the
batch-sharded-draft-logits
branch
from
October 26, 2026 20:30
93eac5c to
86f2f24
Compare
TheEpicDolphin
marked this pull request as ready for review
October 26, 2026 20:43
Member
Author
|
/ci run |
|
✅ Triggered Buildkite CI #97407 for commit ℹ️ This PR is 114 commits behind upstream |
Signed-off-by: Giancarlo Delfin <gdelfin@inferact.ai>
TheEpicDolphin
force-pushed
the
batch-sharded-draft-logits
branch
from
October 26, 2026 22:03
62e2b08 to
c654b5b
Compare
TheEpicDolphin
requested review from
AndreasKaratzas,
mgoin,
tlrmchlsmth and
zyongye
as code owners
October 26, 2026 22:03
Member
Author
|
/ci run |
|
✅ Triggered Buildkite CI #97436 for commit ℹ️ This PR is 125 commits behind upstream |
WoosukKwon
approved these changes
Oct 26, 2026
26 of 35 tasks
Micador
pushed a commit
to Micador/vllm
that referenced
this pull request
Oct 27, 2026
Signed-off-by: Giancarlo Delfin <gdelfin@inferact.ai> Co-authored-by: vllm-agent <claw@inferact.ai> Signed-off-by: Christopher Mackenzie <christopher.mackenzie.software@gmail.com>
Micador
pushed a commit
to Micador/vllm
that referenced
this pull request
Oct 27, 2026
Signed-off-by: Giancarlo Delfin <gdelfin@inferact.ai> Co-authored-by: vllm-agent <claw@inferact.ai> Signed-off-by: Christopher Mackenzie <christopher.mackenzie.software@gmail.com>
6 tasks done
Morrowmake
pushed a commit
to Morrowmake/vllm-cmp170hx
that referenced
this pull request
Oct 27, 2026
…t stubs Upstream vllm-project#63554 (26cbb47) reads draft_logits_num_shards and draft_logits_shard_idx in DFlash2Speculator._generate_draft and DSparkSpeculator._sample_logits; DraftModelSpeculator.__init__ sets them, and the stubs built without it skip that. Set them as upstream does for its own stubs (test_lilicorr.py, test_dspark_topk.py). Harmless before the advance: no code reads them there.
Open
2 of 6 tasks
abmfy
added a commit
to abmfy/vllm
that referenced
this pull request
Oct 30, 2026
… extended specialized decode) Merges vllm-project#61869 head 9a1541d into vllm-project#63921 head cf60138, which already contains vllm-project#63914 and main 8321d11. Textual conflicts, resolved keeping both sides: - specialized_decode/runtime/decode.py: DecodeStep keeps vllm-project#63921's fields with vllm-project#61869's executor union; the executor builds vllm-project#63921's LamportAllGather and vllm-project#61869's Boundaries(all_reduce, mhc_stream). - boundary/boundary.py: vllm-project#63921's step-free Boundaries API on vllm-project#61869's shared mHC stream; first() keeps vllm-project#61869's draft branch. - attention/step.py: vllm-project#63921's early-trigger and pinned wo_b paths, with vllm-project#61869's TP-partial wo_b as every fallback. - runtime/model_state.py, AGENTS.md: both descriptions. - v1/worker/gpu/spec_decode/dflash/speculator.py: main's supports_batch_sharded_draft_logits next to vllm-project#61869's context side stream. - v1/worker/gpu/model_runner.py: main's aux output connector setup, then vllm-project#61869's post-forward graphs. - tests: both sides kept; test_kernels keeps vllm-project#63921's FP32-order slack and vllm-project#61869's finalize-kernel check. Fixes without a textual conflict: - runtime/draft.py: Boundaries calls without the step argument; the draft AttentionStep gets the target's all_gather; DraftDecode.run takes streams. - nvidia/dspark.py: the draft's logits processor gets the target executor's all_gather (vllm-project#63921 replaced the custom all-gather). - spec_decode/dspark/speculator.py: main (vllm-project#63554) allocates draft_logits after load_draft_model, so the fused Markov sampler gate reads use_draft_logits / vocab_size and the draft_logits dtype instead of draft_logits; supports_batch_sharded_draft_logits is False with the fused sampler, whose kernels write the full-size cache. - nvidia/model.py: cast the target step's executor to SpecializedDecode for .embedding. - nvidia/dspark.py: project_context_kv (the side-stream half of the context KV) uses the stacked context_wkv_proj that main's vllm-project#63063 gave precompute_and_store_context_kv, instead of each layer's fused_wqa_wkv, so both paths compute the context KV with the same GEMM. Signed-off-by: Bowen Wang <bowen@inferact.ai> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 tasks done
ischencheng
added a commit
to ischencheng/vllm
that referenced
this pull request
Nov 2, 2026
Follow the speculator config classes (vllm-project#63205): dspark_markov_quantization is a DSpark supported option, validated in DSparkSpeculatorConfig.apply_options and part of its hash factors. Follow the batch-sharded draft logits and sequential block (vllm-project#63554, vllm-project#64356, vllm-project#63886): the walk writes DraftLogitsCache.values at full size, so it keeps the cache replicated and the sequential sampling on every rank. Loading now also fails for a draft model with its own sample_sequential or an output logit transform, and for a non-bf16 draft logits cache. The benchmark's eager arm uses greedy_argmax, as _sample_logits does since vllm-project#57770. Signed-off-by: Chen Cheng <85005591+ischencheng@users.noreply.github.com>
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.
Context
During batch-sharded sampling, the target logits are sharded along the token dimension. The requests are deterministically assigned to ranks using the formula:
req_state_idx % tp_size. So during TP=4, rank 0 only samples (and in the case of spec decoding verifies) requests with state indices: 0, 4, 8, 12, ....Currently, draft model-derived speculators allocate persistent draft logits tensors to hold all requests up to
max-num-seqs, which is wasteful during batch-sharded sampling because onlymax-num-seqs // TPof those request logits are actually used by each rank. Sharding the draft logits can enable us to save GPU memory.This PR
Under batch-sharded sampling, each rank now allocates draft logits only for the requests it verifies. The draft logits cache shrinks from
max_num_seqsrows tocdiv(max_num_seqs, tp_size)rows per rank, and the freed memory goes to the KV cache.Layout. A request's draft logits live only on its owner rank, at row
req_state_idx // tp_size. Two Triton helpers inbatch_shard.pydefine the pair:shard_owner(req_state_idx % num_shards, the existing owner rule) andshard_row(req_state_idx // num_shards). Every write and read of the cache goes through them. With one shard both reduce to the current behavior and compile away.Writes. Drafting stays replicated, so every rank still samples every draft token and no new collectives are added. Each rank writes only the rows it owns:
gumbel_sampletakeslogits_cache_num_shards/logits_cache_shard_idxand masks its cache store to the owner rank.DraftModelSpeculator.sample_draftand DSpark's_sample_logitspass them through._cache_draft_logits_kernel, which writes only the top-k candidate columns, applies the same mask.Reads.
RejectionSamplertakesdraft_logits_num_shards, and the rejection kernels index draft logits withshard_row(req_state_idx, DRAFT_LOGITS_NUM_SHARDS).Allocation. The cache moves from
DraftModelSpeculator.__init__toload_model, so it is allocated once at its final size after the target and draft models are loaded. It is sharded when all of these hold:batch_sharded_sampling_enabled, a new helper that the model runner also uses to create theBatchSharder),supports_batch_sharded_draft_logits(),The model runner passes
speculator.draft_logits_num_shardsto theRejectionSampler. It also asserts that a custom rejection sampler never receives a sharded cache.Speculators that opt in: every one whose cache writes honor the owner mask.
TargetDependentARSpeculator: Eagle, MTP, Gemma4.StandaloneARSpeculatorandMultiModuleMTPSpeculator.DFlashSpeculator, and through it DSpark, XPress, DFlash2 and LiLiCorr.sample_sequential(the AMD DeepSeek-V4.1 path), because those kernels write the full-size cache.Memory freed per rank (TP4,
max_num_seqs=256, probabilistic drafts), from server startup logs:Benchmarks
No regression in accuracy or acceptance rate for any speculator that now writes batch-sharded draft logits.
Setup: 4x GB200, TP4,
draft_sample_method=probabilistic, prefix caching off, natural EOS. Each pair ran 4 server boots in the order base, shard, shard, base. "base" is--enable-batch-sharded-samplingoff and "shard" is on. Startup logs confirmed every shard boot sharded the draft logits cache on all 4 ranks.tests/evals/gsm8k/gsm8k_eval.py, 1319 questions. t0 is temperature 0, one run per boot. t1 is temperature 1 with 3 seeds per boot, 6 runs per arm. The Qwen models use chat completions with thinking disabled.low_entropysubset,throughput_2kconfig, concurrency 64, 256 prompts, temperature 1, top_p 1.GSM8K accuracy
Acceptance length
SPEED-Bench noise check (Qwen3.6-27B + MTP)
Every pair read 0.3–0.6% higher acceptance on SPEED-Bench with sharding. To check whether that was real, I ran a third arm that shards verification but keeps the draft logits cache replicated. I ran each arm with top-k on and off, with 2 boots per arm.
The three arms match. Base alone ranged from 3.254 to 3.279 across boots, so the shift is boot-to-boot noise.
Notes