perf(dspark): add load-aware compact verification capacity - #107
perf(dspark): add load-aware compact verification capacity#107voipmonitor wants to merge 29 commits into
Conversation
Signed-off-by: EanWang211123 <wangyiheng@sangfor.com.cn>
…context DFlash/DSpark build the draft's context KV from target aux hidden states, which only exist for tokens that flow through a target forward pass. Tokens restored from the prefix cache (or a KV connector) at request (re)admission never do, so their draft KV slots are never written — yet the draft attends over the full sequence. With automatic prefix caching and a long shared prefix, the draft reads thousands of uninitialized slots and acceptance collapses to ~0.3% (position-0 only); the same workload with unique prompts reaches ~20%. MTP is unaffected (no context KV), which hid the interaction. Fix: track per request-slot how many tokens were restored at the last (re)admission (RequestState.num_cached_tokens) and hide the restored whole blocks from the draft's attention — the prep kernel shortens the draft seq_lens and a new kernel left-shifts the draft block-table rows in place (safe: input_block_tables are regathered every step, and the shift runs after slot mappings are computed from the unshifted table). Draft KV stores post-RoPE keys at absolute positions, so no position rewriting is needed. Requests without cache hits and dense DFlash/DSpark setups are unaffected (shift 0). Up to block_size - 1 restored slots stay visible when the restored count is not block-aligned (e.g. full-prompt hits). The draft loses the cached prefix from its context (bounded by its training window anyway) in exchange for prefix caching and speculative decoding composing at all. A durable alternative — letting the draft KV cache group participate in prefix-cache block reuse — is left for a follow-up RFC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: giorgiopiatti-dfinity <giorgio.piatti@dfinity.org>
Signed-off-by: mgoin <mgoin64@gmail.com>
…tention Non-causal draft attention (DFlash/DSpark) skips trtllm-gen and runs the FlashInfer prefill wrapper, whose run() is not replay-safe once plan() changes; replaying a full CUDA graph then returns wrong output or an illegal memory access. Only claim UNIFORM_BATCH cudagraph support for causal attention, build draft attention metadata under the draft's attention config, and fall back to eager draft attention when full graphs are unsupported. Signed-off-by: mgoin <mgoin64@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cation
Implements DSpark (arXiv 2607.05147) confidence-scheduled verification with
two capacity enforcement modes and full CUDA graph support:
- Capacity manager with `mask` (pad pruned verify rows; defaults
VLLM_MOE_SKIP_PADDING=1 so MoE kernels skip the pruned rows) and
`varlen` (compact the verifier batch) modes; varlen replays FULL CUDA graphs for
both the target verify step and the DSpark draft query step.
- Paper-faithful Algorithm 1 allocator: capacities are the greedy admission
counts (sum(capacities) == spent budget, hard cap; zero-survival tokens
are never candidates), fixing a threshold-recount tie escape that
disabled the budget under saturated confidence logits.
- Hardware-aware prefix scheduler: `dspark_sps_curve` (profiled
steps-per-second vs verification batch tokens) drives the
theta = tau * SPS(B) argmax stopping rule; `dspark_budget_frac` remains
as an admission upper bound. `dspark_sps_curve="auto"` profiles the
curve at engine init: uniform-decode dummy runs (the DP idle-step path,
which replays the captured verify graph AND the full draft step) are
timed per power-of-two request count up to max_num_seqs after graph
capture, and rank 0's measurements are broadcast so every TP rank builds
the identical table; the allocator captures a flat placeholder table
whose contents are refreshed in place. `dspark_sps_overhead_ms` adds
host/scheduler time the dummy path cannot see.
benchmarks/profile_dspark_sps_curve.py remains for offline measurement.
- Online Sequential Temperature Scaling (`dspark_online_sts`, on by
default with capacity modes): per-position temperatures fitted online by
an ECE grid search over binned rejection-sampler outcomes
(order-preserving, per the paper Sec 3.2.1; identity until observations
accumulate; deterministic one-hot reductions so TP ranks stay bitwise
identical).
- Varlen full-CG correctness fixes: per-request token bound in cudagraph
dispatch, capture/replay buffer-address consistency in the DSA indexer
varlen decode path (forced flatten + persistent indices buffer),
padded-row sizing in the indexer build, TP-deterministic capacity
flushes, and correct handling of the scheduler's -1 draft placeholder
ids in capacity accounting.
- TP-rank determinism fixes for padded draft FULL-graph replays: padding
rows of sample_idx_mapping now carry a -1 inert-row sentinel so replays
never scatter draft logits through stale req-state slot ids
(duplicate-index scatters have undefined write order and silently
diverge per-rank state; with varlen capacity this became a
collective-size-mismatch deadlock), and the online-STS proposal staging
moved out of the captured graph. Worker slot recycling now iterates
finished_req_ids in sorted order and fallback per-request sampling
seeds come from a dedicated RNG stream, removing two more per-rank
divergence hazards. Debug guard: VLLM_DSPARK_TP_CHECK={1,2}
(capacity.py::check_dspark_tp_consistency) cross-checks request-keyed
capacity/STS state across TP ranks each step and fails fast with
per-rank state dumps.
Co-authored-by: OpenAI Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
…capacity-core-validation-20260717 # Conflicts: # vllm/v1/worker/gpu/spec_decode/dflash/speculator.py
📝 WalkthroughWalkthroughAdds DSpark capacity-based speculative decoding with confidence scoring, SPS scheduling, dynamic draft depth, varlen and masked verification, cache-aware DFlash execution, CUDA-graph dispatch constraints, sparse-attention varlen support, profiling utilities, and extensive tests. ChangesDSpark capacity and execution
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
vllm/v1/attention/backends/mla/indexer.py (1)
305-316: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd Google-style sections to the new helper docstrings.
Document parameters and return values using explicit
Args:andReturns:sections.As per coding guidelines, use Google-style docstrings with
Args:/Returns:/Raises:sections.Also applies to: 511-525, 747-763
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@vllm/v1/attention/backends/mla/indexer.py` around lines 305 - 316, Update the docstrings for _needs_varlen_decode and the helpers at the referenced additional locations to use Google-style sections: document every parameter under Args: with types or descriptions, and document the boolean or other return value under Returns:. Preserve the existing behavior and wording outside the added documentation.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@benchmarks/profile_dspark_sps_curve.py`:
- Around line 88-97: Validate profiling parameters before any timing or curve
calculation: require iters > 0, warmup >= 0, and overhead_ms >= 0. Add these
checks at the benchmark entry point covering the timing logic and
curve_breakpoints, and reject invalid values with clear errors before division
or SPS computation.
In `@tests/v1/worker/test_mixed_warmup_gate.py`:
- Around line 24-35: Update the curve in
test_dspark_dynamic_budget_uses_upper_load_sps_knee so its largest overall SPS
drop occurs in the lower half, while retaining an upper-half knee that produces
the expected budget of 160. Keep the assertion and
_derive_dspark_draft_token_budget invocation unchanged.
In `@vllm/model_executor/models/qwen3_dspark.py`:
- Around line 92-94: Update the confidence projection forward path around the
visible forward method to conditionally concatenate markov_embed only when
confidence_head_with_markov is enabled; otherwise pass hidden directly. Ensure
the resulting input dimension matches the projection configuration in both modes
while preserving the existing projection and output squeezing behavior.
In `@vllm/v1/attention/backends/mla/indexer.py`:
- Around line 291-302: Gate varlen CUDA-graph support on the DSpark opt-in by
combining _supports_varlen_paged_mqa_logits() with
_uses_varlen_dspark_capacity(vllm_config) at both hardware capability checks and
the varlen enablement path around the referenced sites. Preserve the existing
hardware checks, but ensure they cannot advertise or enable varlen support
unless the configuration explicitly enables varlen DSpark capacity.
In `@vllm/v1/worker/gpu/model_runner.py`:
- Around line 1171-1174: Make the `self.input_buffers.is_padding` reset in the
surrounding model-runner flow unconditional for every batch, removing the
`verification_capacity_manager`/`VLLM_MOE_SKIP_PADDING`/draft-token guard while
preserving the `[:num_tokens].fill_(False)` operation.
In `@vllm/v1/worker/gpu/spec_decode/dspark/speculator.py`:
- Around line 282-305: Track whether proposal confidence logits are valid for
each request in compute_capacities, marking rows produced under
use_capacity=False or is_profile=True as invalid. Update DSparkOnlineSTS.record
to skip invalid/bypassed/profiled rows so stale confidence buffers are never
calibrated against later verification outcomes; preserve normal recording for
freshly computed confidence logits.
---
Nitpick comments:
In `@vllm/v1/attention/backends/mla/indexer.py`:
- Around line 305-316: Update the docstrings for _needs_varlen_decode and the
helpers at the referenced additional locations to use Google-style sections:
document every parameter under Args: with types or descriptions, and document
the boolean or other return value under Returns:. Preserve the existing behavior
and wording outside the added documentation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d5feef63-0a16-4ca8-b1e3-9bb1e1f4c204
📒 Files selected for processing (58)
benchmarks/profile_dspark_sps_curve.pytests/engine/test_arg_utils.pytests/evals/gsm8k/configs/DeepSeek-V4-Flash-DSpark-varlen-TP4.yamltests/test_config.pytests/v1/attention/test_deepseek_v4_dspark_metadata.pytests/v1/spec_decode/test_acceptance_length_controller.pytests/v1/spec_decode/test_dflash_cudagraph_lifetime.pytests/v1/spec_decode/test_dflash_prefix_cache_masking.pytests/v1/spec_decode/test_dynamic_sd_cug.pytests/v1/worker/test_gpu_block_table.pytests/v1/worker/test_gpu_model_runner_v2_draft_capacity.pytests/v1/worker/test_gpu_sampling_states_seed.pytests/v1/worker/test_mixed_warmup_gate.pytools/pre_commit/generate_attention_backend_docs.pyvllm/config/speculative.pyvllm/engine/arg_utils.pyvllm/envs.pyvllm/model_executor/layers/sparse_attn_indexer.pyvllm/model_executor/models/qwen3_dspark.pyvllm/models/deepseek_v4/common/ops/cache_utils.pyvllm/models/deepseek_v4/nvidia/dspark.pyvllm/models/deepseek_v4/nvidia/flashinfer_sparse.pyvllm/models/deepseek_v4/sparse_mla.pyvllm/platforms/cuda.pyvllm/utils/deep_gemm.pyvllm/v1/attention/backend.pyvllm/v1/attention/backends/flashinfer.pyvllm/v1/attention/backends/mla/indexer.pyvllm/v1/attention/backends/mla/sparse_swa.pyvllm/v1/attention/backends/utils.pyvllm/v1/core/sched/async_scheduler.pyvllm/v1/core/sched/output.pyvllm/v1/core/sched/scheduler.pyvllm/v1/worker/gpu/attn_utils.pyvllm/v1/worker/gpu/block_table.pyvllm/v1/worker/gpu/cudagraph_utils.pyvllm/v1/worker/gpu/dp_utils.pyvllm/v1/worker/gpu/input_batch.pyvllm/v1/worker/gpu/model_runner.pyvllm/v1/worker/gpu/model_states/default.pyvllm/v1/worker/gpu/sample/bad_words.pyvllm/v1/worker/gpu/sample/gumbel.pyvllm/v1/worker/gpu/sample/sampler.pyvllm/v1/worker/gpu/sample/states.pyvllm/v1/worker/gpu/spec_decode/autoregressive/speculator.pyvllm/v1/worker/gpu/spec_decode/capacity.pyvllm/v1/worker/gpu/spec_decode/causal_cascade/speculator.pyvllm/v1/worker/gpu/spec_decode/dflash/cudagraph.pyvllm/v1/worker/gpu/spec_decode/dflash/speculator.pyvllm/v1/worker/gpu/spec_decode/dspark/capacity.pyvllm/v1/worker/gpu/spec_decode/dspark/online_sts.pyvllm/v1/worker/gpu/spec_decode/dspark/speculator.pyvllm/v1/worker/gpu/spec_decode/rejection_sampler.pyvllm/v1/worker/gpu/spec_decode/rejection_sampler_utils.pyvllm/v1/worker/gpu/spec_decode/speculator.pyvllm/v1/worker/gpu/states.pyvllm/v1/worker/gpu/warmup.pyvllm/v1/worker/ubatch_utils.py
…260717' into codex/ff-capacity-core-validation-20260717
|
Canonical-stack update: PR #108 is now a git ancestor of this branch, so the sparse-indexer dedup/lint prerequisite is explicit. The full #107 changed-file set now passes |
…717' into codex/ff-dspark-load-aware-capacity-stack-20260717
Canonical FF + B12X merge handoff (2026-07-17)Pinned bases, rechecked immediately before this handoff:
1. Merge B12X firstThen close superseded lukealonso/b12x#33. It is still draft/conflicting because this account cannot close another author’s PR; #35 is its clean replacement. This exact B12X order was synthesized cleanly and validated with 122 passed, 1 skipped. All four PRs are non-draft, mergeable, clean, and have no unresolved review threads. 2. Merge vLLM into FF in this exact order
The exact sequence was rebuilt from the pinned FF head with no manual conflict resolution. Final validation: 248 passed, 714 skipped, Ruff lint and format clean across all 86 changed Python files, and Merge strategyUse normal merge commits for the stacked pairs:
If FF policy requires squash or rebase merges, stop after rewriting #97, #108, or #105 and rebase the dependent PR (#103 or #107) onto the new FF head before continuing. Otherwise GitHub cannot recognize the tested ancestry and may show duplicate changes. The old mixed #88 and superseded #92/#96 are closed; their canonical replacements are the PRs above. |
|
Superseded by #109. The final code was rebuilt directly on current dev/fathomless-firmament as three clean commits: correctness, optional FP8 draft head, and optional load-aware capacity. #109 preserves the validated behavior while removing the stacked merge history and excluding the unrelated #108 indexer cleanup. |
Summary
Add opt-in load-aware physical draft-depth control for DSpark/DFlash while keeping the configured logical width and target acceptance semantics unchanged.
At low load the controller retains exact fixed-K5 behavior. At the configured activation knee it selects a smaller physical depth, uses compact variable-length verification, and keeps the path on full CUDA graphs. TP rank 0 computes and broadcasts one canonical capacity vector so all ranks replay the same padded shape without a host-wide synchronization.
Dependency
Merge #108 and #105 first. #108 removes a duplicate sparse-indexer helper that otherwise makes the combined changed-file lint set fail; #105 provides the corrected DFlash prefix-cache, request-slot, graph-lifetime, and padding behavior consumed by this capacity path. The PR still targets
dev/fathomless-firmament; after both prerequisites merge, GitHub will reduce this diff to the capacity feature.Behavior and scope
Measured TP2 Lucifer results
Fixed K5 baseline versus load-aware capacity:
Three-run C64 mean was 2725.1 tok/s with SD 11.2. Coding median was 323.0 tok/s, mean 327.5, maximum 359.2. A clean-cache C1/C64/recovery sequence measured 245.2 / 2700.5 / 241.3 tok/s; a 120-second C64 run measured 2751.4 tok/s.
Rejected approaches
Validation
git diff --checkpassesThis is extracted from the old mixed #88 branch so the capacity optimization can be reviewed and merged independently.
Summary by CodeRabbit
New Features
Bug Fixes
Tests