Skip to content

[GDN] Amortize ReplaySSM checkpoint materialization - #35544

Merged
YAMY1234 merged 13 commits into
sgl-project:mainfrom
YAMY1234:agentx-upstream/gdn-replayssm-history-20260819
Sep 4, 2026
Merged

YAMY1234 merged 13 commits into
sgl-project:mainfrom
YAMY1234:agentx-upstream/gdn-replayssm-history-20260819

Conversation

@YAMY1234

@YAMY1234 YAMY1234 commented Aug 19, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

ReplaySSM speculative decoding currently materializes the full GDN checkpoint state on every target step, even though most speculative iterations only append a small amount of history. Deferring that work removes repeated state materialization from the decode critical path.

Modifications

  • Store speculative GDN updates in a bounded circular history and materialize the checkpoint once per accepted verify window.
  • Preserve recurrent BF16 behavior with compensated high/low representations for the compact update vector and normalized key.
  • Keep replay history and cursors request-scoped while keeping the physical checkpoint Mamba-slot-scoped.
  • Size ReplaySSM scratch by active request capacity, and preserve reset, donation, retraction, and recovery lifetimes.

The review scope is limited to the ReplaySSM kernel, the request/state ownership and accounting needed by that kernel, and one existing accounting test. The larger experimental compatibility framework and additional defensive abstractions are not part of this PR.

Accuracy Tests

  • Nine targeted GPU tests passed, covering existing accounting/support checks plus BF16 and FP32 64-window recurrent comparisons and request-capacity allocation.
  • A monolithic aggregate TP4 serving smoke test completed successfully and returned exactly 512 output tokens.
  • ReplaySSM scratch is 0.857 GB/GPU versus 0.849 GB/GPU before the PR. CUDA-graph startup available memory is 36.129-36.131 GB versus 36.143-36.145 GB; the earlier approximately 3.6 GB/GPU headroom regression is removed.

AIME25 128K x4

The comparison uses Qwen3.5 on one 4-GPU node, aggregate TP4/DP1/PP1/EP1 serving, MTP, ReplaySSM, BF16 SSM state, 128K context, four repeats, and the same 30 problems.

Configuration ReplaySSM pass@1 majority@4 pass@4
Before PR: old ReplaySSM implementation Enabled 86.67% 88.33% 96.67% (29/30)
Current ReplaySSM implementation Enabled 87.50% 96.11% 100.00% (30/30)
Configuration Repeat 0 Repeat 1 Repeat 2 Repeat 3 Total
Before PR: old ReplaySSM implementation 28/30 26/30 25/30 25/30 104/120
Current ReplaySSM implementation 26/30 27/30 26/30 26/30 105/120

The current run produced all 120/120 predictions with zero harness errors and 3,019,855 completion tokens.

AIME25 128K x8

For additional confidence, the old and current ReplaySSM implementations were rerun with the same serving/evaluation contract and eight repeats. pass@N is the fraction of the 30 problems with at least one correct answer among N attempts; it is not the average accuracy across repeats.

Configuration ReplaySSM pass@1 majority@8 pass@8
Before PR: old ReplaySSM implementation Enabled 91.67% 96.67% 100.00% (30/30)
Current ReplaySSM implementation Enabled 92.08% 97.78% 100.00% (30/30)
Configuration R0 R1 R2 R3 R4 R5 R6 R7 Total
Before PR: old ReplaySSM implementation 28/30 26/30 28/30 29/30 28/30 27/30 27/30 27/30 220/240
Current ReplaySSM implementation 26/30 26/30 28/30 28/30 27/30 29/30 28/30 29/30 221/240

Both arms produced all 240/240 predictions with zero harness errors and passed all 30 problems at pass@8. The current implementation is +1/240 in total correct attempts (92.08% versus 91.67%).

Logits and long-sequence stability

  • Four independent AIME27 traces completed successfully; all four produced the correct answer (248).
  • Over the token-aligned common prefix with the old implementation (406 tokens on average), top-1 agreement is 99.92%, mean top-5 Jensen-Shannon divergence is 3.82e-4, and mean absolute chosen-logprob delta is 7.42e-3.
  • An independent rerun of the compact implementation gives 100% top-1 agreement and a 7.61e-3 mean absolute chosen-logprob delta over its common prefix, placing the old-versus-current delta at the same scale as run-to-run variation.
  • After token paths diverge, the values are confidence diagnostics rather than token-aligned numerical error. Across a 34,816-token horizon, chosen-logprob variance decreases from 0.0194 to 0.0105 and entropy decreases from 0.0706 to 0.0464; there is no evidence of error or variance accumulating with generation length.

Speed Tests and Profiling

  • Setup: Qwen3.5-397B-A17B-NVFP4 on one node with 4 GB300 GPUs in DP4, MTP with 6 draft tokens, TRTLLM-MHA split 1, ReplaySSM extra_buffer, radix-cache page size 64, and the same frozen 112-request cohort in both arms.
  • Workload: every request uses exactly 88,737 input tokens and generates exactly 8,192 output tokens. Requests are prefetched before measurement; no new prefill is admitted during the pure-generation interval.
  • Acceptance criteria: 112/112 HTTP 200 responses, routed distribution [28, 28, 28, 28], achieved batch 28 on every rank, CUDA graph enabled, queue depth 0, no replenishment, and measurement ending before the first completion.
Variant Natural mean acceptance length Mean context/request Target-step time (ms) Fixed-AL 4.8 output tokens/s/GPU Change vs. baseline Observed output tokens/s/GPU Change vs. baseline
Before PR: old ReplaySSM implementation 4.32726 6,311.26 39.74513 3,381.622 - 3,048.695 -
Current ReplaySSM implementation 4.27298 6,201.45 37.94529 3,541.988 +4.742% 3,153.163 +3.427%
  • The current implementation reduces target-step time by 4.528% versus the baseline.
  • Natural acceptance length changes by -1.254% and mean context by -1.740%; the fixed-AL metric normalizes acceptance to 4.8, while observed throughput remains +3.427%.
  • Rank CV for fixed-AL throughput is below 0.6% in both arms.
  • Output tokens/s/GPU is an engine-side pure-generation metric from the fixed-B28 target-step window, not a service-level AIPerf total-throughput metric.

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ✅ Run #33840060530
Latest PR Test (Extra): ❌ Run #33840060375
Latest PR Test (AMD ROCm 7.2): ⏳ Run #33840060550

Copy link
Copy Markdown
Collaborator Author

/rerun-test test/registered/models_e2e/test_qwen35_fp4_mtp.py::TestQwen35FP4MTPReplaySSM.test_gsm8k test/registered/attention/unittests/gdn/test_gdn_replayssm_spec_fold.py test/registered/attention/unittests/gdn/test_gdn_cutedsl_ring_verify.py test/registered/attention/unittests/gdn/test_linear_replayssm_decode.py

@github-actions

github-actions Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test/registered/models_e2e/test_qwen35_fp4_mtp.py::TestQwen35FP4MTPReplaySSM.test_gsm8k test/registered/attention/unittests/gdn/test_gdn_replayssm_spec_fold.py test/registered/attention/unittests/gdn/test_gdn_cutedsl_ring_verify.py test/registered/attention/unittests/gdn/test_linear_replayssm_decode.py:

🚀 4-gpu-b200 (1 test): ✅ View workflow run

cd test/ && python3 registered/models_e2e/test_qwen35_fp4_mtp.py TestQwen35FP4MTPReplaySSM.test_gsm8k

🚀 1-gpu-h100 (3 tests): ✅ View workflow run

cd test/ && python3 registered/attention/unittests/gdn/test_gdn_replayssm_spec_fold.py
cd test/ && python3 registered/attention/unittests/gdn/test_gdn_cutedsl_ring_verify.py
cd test/ && python3 registered/attention/unittests/gdn/test_linear_replayssm_decode.py

@YAMY1234

Copy link
Copy Markdown
Collaborator Author

Added the Replay SSM AIME results. There is no accuracy regression.
image

@YAMY1234
YAMY1234 force-pushed the agentx-upstream/gdn-replayssm-history-20260819 branch from 618b9ab to 150c91c Compare August 28, 2026 05:58
@YAMY1234
YAMY1234 marked this pull request as ready for review August 29, 2026 04:42
…35544-conflict-resolution-20260828

# Conflicts:
#	python/sglang/srt/server_args.py
@Qiaolin-Yu

Qiaolin-Yu commented Aug 31, 2026 •

Copy link
Copy Markdown
Collaborator

Added the Replay SSM AIME results. There is no accuracy regression. image

could we run more pass? maybe repeat 8 or repeat 16. Does the pass@4 mean average of 4 runs? It seems the pass@4 is higher but total is lower?

k_raw_tile = k_tile
q_tile = (q_tile * (q_rnorm * scale)[:, None]).to(h0.dtype.element_ty)
k_tile = (k_tile * k_rnorm[:, None]).to(h0.dtype.element_ty)
q_tile = (q_tile * (q_rnorm * scale)[:, None]).to(q.dtype.element_ty)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we downgrade the precision of q_tile/k_tile, b_d_scaled, and khist_tile, right? Is this expected?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. The FP32-checkpoint path was unintentionally lowering these operands; 9168a3f7dd restores its FP32/TF32 path while leaving the BF16 hot path unchanged. The BF16 and FP32 64-window GPU comparisons both pass.

elif enable_linear_replayssm_spec and ssm_dtype != torch.float32:
# Low parts of compact D and normalized K. A BF16 checkpoint
# materializes these once per accepted verify window.
replayssm_rawv = torch.zeros(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we hold low parts for ssm_dtype==bf16 but not for ssm_dtype==fp32?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The low parts should follow the compact ring dtype, not the checkpoint dtype, because spec-verify replayssm_d/k use the activation/conv dtype. 9168a3f7dd changes allocation and accounting to ring_dtype != torch.float32, including the FP32-checkpoint + BF16-ring case.

…35544-conflict-resolution-20260828

# Conflicts:
#	python/sglang/srt/mem_cache/mamba_radix_cache.py
#	python/sglang/srt/mem_cache/memory_pool.py
@YAMY1234

YAMY1234 commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

pass@4/pass@8 mean the fraction of the 30 problems with at least one correct answer among 4/8 attempts, not the average repeat accuracy. I ran the matched 128K x8 comparison: old ReplaySSM was 220/240 (91.67%) and current was 221/240 (92.08%); both completed 240/240 predictions with zero harness errors and reached pass@8 100% (30/30). The PR description now includes both x4 and x8 tables.

@YAMY1234

YAMY1234 commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Sep 1, 2026
@Qiaolin-Yu Qiaolin-Yu self-assigned this Sep 2, 2026
@YAMY1234

YAMY1234 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

@YAMY1234
YAMY1234 merged commit db89f63 into sgl-project:main Sep 4, 2026
221 of 261 checks passed
StevenChenSE pushed a commit to StevenChenSE/sglang that referenced this pull request Sep 6, 2026
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 24, 2026
…ernel strided q/k/v views

Upstream 9fdb717 (main only, not in v0.5.20). On the 27B's D group every
DFLASH verify step ran fused_qkv_split_gdn_prefill on every GDN layer, one
copy kernel per layer per step, although the Triton verify kernel
(fused_sigmoid_gating_delta_rule_update) reads q/k/v with their token strides
(stride_q = q.stride()[1], runtime int, dense head axis). Target verify now
passes torch.split views of the post-conv mixed_qkv when the chosen verify
kernel opts in; prefill keeps the fused split (FLA chunk kernels want dense).

Ported: LinearAttnKernelBase.supports_strided_target_verify_qkv (False),
TritonGDNKernel opt-in, GDNKernelDispatcher._get_target_verify_kernel +
target_verify_supports_strided_qkv, the routing in forward_extend.

Adapted: the fork has no ReplaySSM spec-fold / circular verify routes
(sgl-project#28695/sgl-project#35544 not on this line), so the routing is the dispatcher
capability alone, decided inline per forward instead of in metadata init.
The Triton opt-in is limited to the CUDA/HIP Triton kernel (NPU/CPU/XPU
substitute other implementations in gdn_triton.py). Tree verify (topk>1)
routes to the Triton tree kernel, which also opts in; FlashInfer/CuTe
kernels inherit False.

Tests (CPU, incg cgroup):
test/registered/unit/layers/attention/test_gdn_verify_strided_qkv_33778.py
- dispatcher capability (linear + tree Triton opt in, attribute-less kernel
  does not);
- GDNAttnBackend.forward_extend TARGET_VERIFY: the verify kernel receives
  views sharing mixed_qkv's storage (token stride = qkv width), the fused
  split is never called; EXTEND still calls it;
- the production verify kernel under TRITON_INTERPRET: strided views vs dense
  copies (ratio 3, 2 requests x 4 draft tokens, intermediate states cached,
  state update disabled) -> output and intermediate states bit-identical.
4 passed. On the base sources the capability and verify-routing tests fail.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 25, 2026
…ned to the recurrent verify

27B ReplaySSM package, slice S1 of REPLAYSSM_PLAN.md (D-KV: the ring replaces
the per-draft "speculative intermediate state"). No wiring, no flag yet --
nothing on any serving path changes.

Ported verbatim from upstream main (chain sgl-project#28695 -> sgl-project#32692 -> sgl-project#36970 ->
sgl-project#35544): sglang/kernels/ops/attention/fla/gdn_replayssm_spec_decode.py ->
this line's srt/layers/attention/fla/ (only the fla.utils import moved):
circular verify kernel (output from checkpoint + compact d/k/g history),
compact commit (materializes accepted windows, hi/lo low parts for 16-bit
checkpoints, track rows snapshot the crossing state), cursor advance/reset.
configs/mamba_utils.py: BaseLinearStateParams.replayssm_ring_bytes_per_req
(upstream helper; the shape is the rank's own, so uneven GDN TP prices every
rank correctly) -- the one formula the mixin and the planner will share (S5).

Why the pin: upstream runs the GDN route only behind the EAGLE/MTP commit
and refuses DFLASH for non-KDA models (kv_cache_configurator), so GDN +
DFLASH (the 27B) never ran upstream. The test drives both production kernels
under TRITON_INTERPRET against this line's DFLASH verify kernel
(fused_sigmoid_gating_delta_rule_update with per-step intermediate states +
the scatter of the last accepted step).

Test (CPU, incg cgroup):
test/registered/unit/layers/attention/test_gdn_replayssm_spec_equivalence_s1.py
- fp32 checkpoint: verify rel 2.6e-7, track snapshot 1.6e-7, second verify
  from checkpoint + ring history 2.8e-7 vs the recurrent path; cursors [3,0,1];
- 16-bit (fp16 standing in for bf16): verify 4.2e-4 vs recurrent 2.6e-4 of the
  fp32 truth, committed checkpoint 3.7e-4 = the recurrent 16-bit rounding,
  track 3.0e-4; cursors back to 0 (fold every commit); other slots bit-equal;
- ring price of a 27B rank row (HV 18 / H 6, bf16, L 16): 9.5 MB vs 226 MB
  of intermediate state for 8 draft steps.
3 passed. Mutant (cursor advance one short): both equivalence tests red.
bf16 itself is a metal gate (plan): acceptance, needle, greedy A/B.

27B line (desk/27b-up-replayssm-line-0924): cherry-picked from 420c547
(desk/27b-up-replayssm-0924, on the NF-based staging line fa757eb) onto
e15e760, the 27B line (xsn411 829ebd0 + upstream ports + P without draft
compute + record filter). Nothing to adapt: the kernel file is new, and
configs/mamba_utils.py, fla/ and the DFLASH verify kernel it is pinned against
are identical on both lines.

Tests on this line (CPU, incg cgroup, S1 tree):
test_gdn_replayssm_spec_equivalence_s1.py 3 passed. Interpreter vs the
recurrent DFLASH verify: fp32 verify rel 2.63e-7, track snapshot 1.57e-7,
second verify from checkpoint + ring history 2.79e-7 (all < 4e-7), cursors
[3,0,1]; 16-bit verify 4.18e-4 (recurrent 2.65e-4 of the fp32 truth),
committed checkpoint 3.71e-4 == recurrent 3.71e-4, track 2.96e-4, cursors
[0,0,0], other slots bit-equal -- the values the NF line reported.

(cherry picked from commit 9431003)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jit-kernel memory-pool run-ci CI: run the baseline test suite on this PR unified-radix-cache

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants