Skip to content

[Fix] Mask unused ReplaySSM history before checkpoint arithmetic - #42688

Open
aurickq wants to merge 3 commits into
sgl-project:mainfrom
aurickq:fix/replayssm-unused-history
Open

aurickq wants to merge 3 commits into
sgl-project:mainfrom
aurickq:fix/replayssm-unused-history

Conversation

@aurickq

@aurickq aurickq commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The compact ReplaySSM checkpoint kernel reads the whole history ring, then gives unused entries zero decay. Reused ring storage can contain NaN or infinity, and multiplying those values by zero still contaminates the checkpoint. A tracked checkpoint can also be corrupted by a nonfinite update that belongs to the active history but lies after the tracked prefix.

For example, a ring with four valid updates and a stale NaN in its fifth entry should produce the same checkpoint as clean unused storage. A checkpoint tracking the first three updates should remain finite when only the fourth update is nonfinite.

Modifications

Mask unused history loads before arithmetic, and mask keys and updates to the tracked prefix before its matrix products. Nonfinite values within the active history still propagate; this prevents unrelated history from contaminating checkpoints rather than sanitizing valid inputs or existing state.

Add synthetic regression tests for all history fields, NaN and positive/negative infinity, ring wraparound, residual modes, shorter tracked prefixes, empty history, and untouched/null slots.

Accuracy Tests

All 111 regression cases passed on a CUDA GPU (no failures or skips), using pytest -q test/registered/attention/unittests/gdn/test_gdn_replayssm_compact_commit.py. File-scoped pre-commit and Python syntax checks also passed.

Speed Tests and Profiling

Not benchmarked for this revision. The change adds load/operand masks within the existing checkpoint kernel and does not add a kernel launch.


CI States

Latest PR Test (Base): 🚫 Run #39342505937
Latest PR Test (Extra): ❌ Run #39342505801
Latest PR Test (AMD ROCm 10): ❌ Run #39342505871

@aurickq
aurickq marked this pull request as ready for review October 6, 2026 18:41
@Qiaolin-Yu Qiaolin-Yu self-assigned this Oct 7, 2026

@ch-wan ch-wan left a comment

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.

Reproduced the issue and checked the fix independently on H200 (torch 2.14.1+cu130, triton 3.8.0), on main f47d895 with this PR's kernel change applied. The kernel file has not changed on main since this PR's base.

  • Kernel test. test_gdn_replayssm_compact_commit.py against unpatched main: 84 failed, 27 passed. With this PR: 111 passed.
  • End to end. Qwen3.8-27B (BF16) on one H200 with --kv-cache-dtype fp8_e4m3 --attention-backend flashinfer, MTP (--speculative-algorithm EAGLE --speculative-num-steps 3 --speculative-eagle-topk 1 --speculative-num-draft-tokens 4) and --enable-linear-replayssm-spec. Test-only instrumentation wrote a single NaN into one request's hidden state at one verify step; each run then served 384 chat and shared-prefix requests. Requests with any nonfinite output logprob:
seed main this PR
11 5 1
12 7 1
13 5 1

On main, every extra bad request sits in the same request-pool slot as the injected one, and first turns nonfinite at the first GDN layer after the injection point. The stale ring entries poison each later request that is assigned that slot. With this PR, the NaN stays in the request it was injected into.

So on main, one transient NaN anywhere upstream becomes recurring NaNs for unrelated later requests. That may be worth a line in the description: the fix matters to anyone serving GDN models with --enable-linear-replayssm-spec.

The verify kernel and the decode ring kernel already mask their history loads, so the compact commit is the only place that needed this.

LGTM once CI passes.

@Qiaolin-Yu Qiaolin-Yu added run-ci CI: run the baseline test suite on this PR bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) labels Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Required checks failed on b6448d1; this PR cannot be merged until they pass.

PR title (run)

PR title must use "[Type] scope: title" or "[Type] [Hardware] scope: title". Type must be one of: Feat, Fix, Refactor, Perf, Doc, Test, CI, Chore. Scope is required and may be any module or model name, with unrestricted letter case (letters, numbers, dots, underscores, slashes, or hyphens). Hardware is optional; use a single tag without spaces. Use uppercase hardware tags (AMD, NVIDIA, NPU, MI300X), except for exactly these four spellings: Intel, Apple, Huawei, Ascend. Reverts must use GitHub's default format: Revert "original title". Use one space between fields and after the colon, with a non-empty title and no leading or trailing whitespace. Examples: "[Fix] attention: Fix KV cache indexing", "[Feat] qwen3: Add model support", "[Perf] [AMD] flash-attn: Optimize decode performance".
Edit the title; the check reruns on its own.

detain added a commit to detain/sglang that referenced this pull request Oct 29, 2026
gdn_replayssm_compact_commit_kernel loaded the whole ring window
(MAX_CACHE_LEN rows) regardless of n_history. Reused ring storage can
hold non-finite values in slots beyond the tracked history, and zero
decay cannot suppress NaN/Inf there: 0*inf contamination poisoned the
committed checkpoint. The loads are now masked by history_mask (with the
fold math simplified to rely on it), and in the track path rows outside
the tracked prefix are explicitly zeroed before the dot products, so a
nonfinite update AFTER the tracked prefix can no longer corrupt it.

File was byte-identical to the PR base, so the kernel hunk lands
verbatim. The PR's test file is 111 CUDA cases (no CPU-exercisable
logic); ported with a module-level CUDA skip per the repo convention so
CPU CI collects it as one clean skip (verified: ungated copy collects
exactly 111 cases).

(cherry picked from commit 99a8da4)
PR: sgl-project#42688 (open, ported)
(cherry picked from commit 5757ec5f59bc8a3957d4157d4752a7ab8a9a8c0e)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) jit-kernel run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants