perf(pytorch): reduce speculative decoding pre/post-processing overhead - #4877
Open
grimoire wants to merge 10 commits into
Open
perf(pytorch): reduce speculative decoding pre/post-processing overhead#4877grimoire wants to merge 10 commits into
grimoire wants to merge 10 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR targets PyTorch speculative decoding performance by reducing non-forward overhead (CUDA graph buffer fills, DSA/NSA metadata prep, rejection sampling, and stopping criteria), primarily via kernel fusion and backend routing while keeping model-forward behavior unchanged.
Changes:
- Introduces fused Triton kernels to batch-update CUDA graph input buffers and DSA indexer metadata, reducing kernel launch counts.
- Refactors speculative rejection sampling into a backend-dispatched implementation (CUDA + portable Torch), including an all-greedy fast path and host-side greedy detection (
has_greedy) to avoid syncs. - Updates speculative stopping criteria to reuse rejection-sampling results (
num_rejected_tokens) instead of rescanning placeholder tokens; expands/updates unit tests accordingly.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/pytorch/spec_decode/test_strategies.py | Extends sampling strategy tests to cover host-side greedy summary (has_greedy). |
| tests/pytorch/spec_decode/test_spec_agent.py | Adds rejection-sampling delegation test; updates sampling-input selection tests. |
| tests/pytorch/spec_decode/test_reject_sample.py | Reworks rejection-sampling tests to use backend RejectionSampler instead of direct kernels/helpers. |
| tests/pytorch/spec_decode/test_guided_spec_decode.py | Removes slicing-related tests now that slicing helper is dropped; updates docstring/comments. |
| tests/pytorch/engine/test_model_agent.py | Updates spec-agent build expectations around backend config and proposer/follower behavior. |
| lmdeploy/pytorch/strategies/ar/sampling.py | Computes and stores has_greedy summary in SamplingInputs. |
| lmdeploy/pytorch/strategies/ar_spec/model_agent.py | Updates stopping criteria to use num_rejected_tokens and reduces metadata work. |
| lmdeploy/pytorch/spec_decode/spec_agent.py | Removes _slice_sampling_inputs; routes decode rejection sampling through RejectionSampler. |
| lmdeploy/pytorch/spec_decode/reject_sampler.py | New backend-dispatched rejection-sampling wrapper + bonus-policy selection helper. |
| lmdeploy/pytorch/spec_decode/base.py | Removes base-class initialization of rejection sampler (now owned by concrete agent). |
| lmdeploy/pytorch/models/utils/cudagraph.py | Replaces multiple buffer updates with a single fused kernel call for common inputs. |
| lmdeploy/pytorch/model_inputs.py | Adds _get_q_start_loc fast path for uniform layouts; gates window-size adjustment. |
| lmdeploy/pytorch/kernels/cuda/step_metadata/fill_graph_common_inputs.py | Adds fused Triton kernel to fill common CUDA graph buffers in one launch. |
| lmdeploy/pytorch/kernels/cuda/step_metadata/fill_dsa_indexer_metadata.py | Adds Triton kernel to build DSA indexer KV-lengths and optional expanded block tables. |
| lmdeploy/pytorch/kernels/cuda/step_metadata/init.py | Introduces package init for new CUDA step-metadata kernels. |
| lmdeploy/pytorch/kernels/cuda/rejection_sampling.py | Adds Triton kernels for greedy/random rejection sampling and recovered-token sampling. |
| lmdeploy/pytorch/engine/logits_process.py | Extends SamplingInputs with has_greedy host-side routing hint. |
| lmdeploy/pytorch/backends/rejection_sampling.py | Adds backend interface + shared placeholder constant for rejection sampling. |
| lmdeploy/pytorch/backends/nsa.py | Allows caller-supplied indexer_kv_seqlens to avoid redundant computation. |
| lmdeploy/pytorch/backends/default/rejection_sampling.py | Implements portable Torch rejection sampling backend. |
| lmdeploy/pytorch/backends/default/op_backend.py | Registers OpType.RejectionSampling for the default backend. |
| lmdeploy/pytorch/backends/cuda/rejection_sampling.py | Implements CUDA rejection sampling backend using the new Triton kernels. |
| lmdeploy/pytorch/backends/cuda/op_backend.py | Registers OpType.RejectionSampling for the CUDA backend. |
| lmdeploy/pytorch/backends/cuda/nsa.py | Moves DSA indexer metadata prep into CUDA backend and enables graph-buffer aliasing. |
| lmdeploy/pytorch/backends/base.py | Adds OpType.RejectionSampling enum entry. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
CUHKSZzxy
reviewed
Aug 20, 2026
CUHKSZzxy
approved these changes
Aug 20, 2026
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
GLM-5.2 MTP profiling showed that work outside model forward was a material
part of each speculative decoding step. In particular,
build_step_context,fill_buffers_cudagraph, rejection sampling, and stopping criteria launchedmany small CUDA kernels.
This PR reduces that overhead without changing model-forward implementations,
adding CUDA graphs, or introducing additional streams.
Changes
input_idsfor MoE load balancing.cumulative lengths together.
kv_seqlensfor single-token graph replay.graph and eager decode.
StepContextmetadata kernels..item()synchronization.placeholder tokens repeatedly.
The change preserves the existing CUDA graph count, DP ordering,
sleep/wakeup behavior, and weight-loading lifecycle.
Performance
Measured with a four-H200, TP=4, batch-size-128, MTP-5 reduced GLM-5.2 model
using FP8 KV cache:
2.019 ms -> 1.102 ms.118 -> 8.0.49%.1.286 ms -> 0.705 ms.0.505 ms -> 0.371 ms.The direct range and launch reductions are repeatable. End-to-end improvements
are smaller because model forward remains dominant.