Repository navigation
[Spec][PP] Fix hybrid recurrent-state commit and micro-batch pairing under PP x speculative decoding - #40001
Merged
Conversation
Source: PP+MTP verification L1; follow-up fix to upstream PR sgl-project#30775. (cherry picked from commit 4372b6a)
Source: PP+MTP verification L1; follow-up fix to upstream PR sgl-project#30775. (cherry picked from commit 815bf6a)
(cherry picked from commit 8981246)
(cherry picked from commit abaeec1)
(cherry picked from commit acc87bb)
(cherry picked from commit 3affe61)
(cherry picked from commit aa30bd4)
(cherry picked from commit aade19e)
YAMY1234
force-pushed
the
pp-spec-hybrid-state-fix
branch
from
September 18, 2026 00:31
f55daf7 to
677132f
Compare
4 of 5 tasks
…e-fix-r3-main # Conflicts: # python/sglang/srt/managers/scheduler_pp_mixin.py
The standalone file duplicated TestPPSpecConsistency's launch/eval flow with 32 questions and an exact-match threshold. The hybrid target is now a subclass that reuses the 256-question run, the 2% score floor and the accept-length floor; MTP drafts a chain, so the tree case is skipped.
Collaborator
Author
|
/tag-and-rerun-ci |
…1fa1-sync-00986c81 # Conflicts: # python/sglang/srt/managers/scheduler_pp_mixin.py
…ybrid-state-fix
Resolve the one conflict in test/registered/unit/spec/test_ngram_mamba_verify_update.py: keep the two PP verify-source-row test classes added by this PR and drop TestConvWindowDedupLayout, which sgl-project#41286 removed on main.
Brings in sgl-project#39643 (model-agnostic last-stage draft embedding, f209e67), which TestPPSpecHybridConsistency depends on: without it the PP1 draft worker raised "none of (model.embed_tokens.weight, ...) found in checkpoint at Qwen/Qwen3.5-4B". No conflicts; merged tree = main + the 18 PR files.
Resolve the import-block conflict in python/sglang/srt/models/qwen4_exp.py: main (sgl-project#41555) renamed sglang.srt.layers.communicator to layer_boundary and already imports get_attn_tp_context from there, so the branch-side import of the deleted module is dropped; the linear.utils import added by this PR is kept. No other file needed manual resolution.
Clean merge (no conflicts). Picks up sgl-project#41618 (FakeKVReceiver.ensure_abort_notified), which fixes the main regression in test_disaggregation_optimistic_prefill.py that failed the previous base run.
Collaborator
|
/rerun-failed-ci |
Collaborator
Author
Open
2 of 5 tasks
10 of 21 tasks
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
#30775 enabled PP x speculative decoding (
SGLANG_ENABLE_PP_SPEC=1). On hybrid linear-attention targets (KDA / GDN / Mamba-style recurrent state) it is currently incorrect in two independent ways, both found while validating PP + MTP across model families (tracking: #39634):run_eagle_verify; earlier stages only run target verify and relay the result, so their recurrent state stays at the pre-verify value while the last stage advances. GLM-5.3-Flash PP2 + MTP: GSM8K 0.745 vs 0.990 without spec, accept length looks normal (3.5), so the bug is silent.forward_metadata, which on a non-last stage still belongs to the previous forward (slot(i-1) % N), and the KDA verify scratch was written at positional rows0..bs-1that in-flight micro-batches overwrite before the relay returns. With PP2(i-1) % 2 == (i+1) % 2, so both errors are masked; with PP4 the first concurrent batch crashes withindices length mismatch: dst_indices_raw.shape[0]=7 vs step_indices_raw.shape[0]=12, and after fixing only the destination the accuracy is 0.485.Pipeline-parallel speculative decoding can receive verify acceptance after a later micro-batch has overwritten positional hybrid-state scratch. When
SGLANG_ENABLE_PP_SPEC=1, hybrid verify scratch and the delayed recurrent-state commit therefore use request-pool stable rows; with the gate off, the existing positional / active-metadata path is unchanged.Modifications
scheduler_pp_mixin: relayaccept_indexunconditionally (chains too). On non-last stages_pp_spec_commit_relayed_acceptcommits the acceptance relayed for that same micro-batch (samecommit_mamba_states_after_verifythe last stage uses) beforeseq_lensadvances, then compacts the accepted KV. The scheduler snapshots each PP-spec micro-batch and clonesreq_pool_indices(the Mamba-track tensor is rebound, never mutated, so the snapshot shares it).pp_spec_stable_rows_enabled()(layers/attention/linear/utils.py) is the single predicate for the stable-row policy; kernels takestable_rows=from the backend instead of reading the env.hybrid_linear_attn_backend,mamba_state_scatter_triton): under the PP-spec gate, when the caller carriesreq_pool_indices, rebuild and translate destination state indices from the matching forward snapshot instead of the activeforward_metadata; with the gate off, or without rows, the existing path runs.kda_backend,gdn_backend,gdn_flashinfer): the request-pool slot is the stable scratch row (padding still goes to the discard row); row selection is shared inselect_verify_intermediate_state_indices. The FlashInfer GDN MTP kernel writes positional0..B-1and takes no row indices, so the PP-spec path uses a positional scratch and remaps to the stable rows after the kernel. KDA PP verify falls back from the FlashInfer kernel to Triton; ReplaySSM verify reads its source rows from the same stable mapping (src_indices_raw/dst_indices_rawacross backends).ple_state_pool,qwen4_exp) gets the same request-row pairing.No change to the attention math or the acceptance algorithm; only where state is stored and how relayed results are paired.
Accuracy / validation (GB300, GSM8K 200 questions,
SGLANG_ENABLE_PP_SPEC=1, EAGLE/MTP steps 3 topk 1)test/registered/e2e/pp/test_pp_spec.py::TestPPSpecConsistencyRe-verified after merging main
d229952(with #39643 applied, which the aggregate path needs on main for the GLM / Qwen3.5 draft-embedding key): GLM-5.3-Flash PP2xTP2 + MTP GSM8K-200 0.990 / AL 3.40; Qwen3.5-4B hybrid e2e (TestPPSpecHybridConsistency, GSM8K-256): no-PP 0.8906 / accept 3.33, PP2 0.8906 / accept 3.33.Tests:
test/registered/e2e/pp/test_pp_spec.py::TestPPSpecHybridConsistency(Qwen3.5-4B with its own MTP head,--language-only, PP1 vs PP2 on GSM8K-256 with the shared 2% score and accept-length floors) replaces the earlier pipeline mocks; unit cases stay intest_ngram_mamba_verify_update.py(scatter source rows, FlashInfer row remap, row selection, ReplaySSM fold and circular helpers, PLE rows) andtest_kda_helion_dispatcher.py(KDA Triton fallback).Checklist
CI States
Latest PR Test (Base): ✅ Run #36697732993
Latest PR Test (Extra): ✅ Run #36697732150
Latest PR Test (AMD ROCm 10): ⏳ Run #36697733207