[Triton][GDN] Add in-place state scatter + h output to VK chunk#4058
[Triton][GDN] Add in-place state scatter + h output to VK chunk#4058hsthe29 wants to merge 3 commits into
Conversation
chunk_gated_delta_rule_opt_vk already computes the gated-delta-rule chunk with a
V-major [N, H, V, K] hidden state and GVA (Hg k-heads / H v-heads), but it could
not serve a paged / radix recurrent-state pool: it took a dense per-sequence
initial_state and discarded the per-chunk hidden-state snapshots.
Add two capabilities, both gated behind new optional args (defaults preserve the
existing behavior byte-for-byte):
- initial_state_indices: in-place scatter. When given, the h-scan reads the
initial state from, and writes the final state back to, the SAME pool at
slot initial_state_indices[i_n] ((index * H + i_h) * V * K) instead of a
dense [N, ...] buffer. Only the Triton VK hidden-state path supports it
(use_chunk_hip / use_chunk_flydsl raise). Mirrors the sgl-fla VK h-scan.
- return_h: expose the per-chunk hidden-state snapshots [B, NT, H, V, K]
(already produced internally) so callers can track SSM state at chunk
boundaries (radix / prefix cache).
Threaded through the kernel (chunk_gated_delta_rule_fwd_kernel_h_opt_vk), the
wrapper (chunk_gated_delta_rule_fwd_h_opt_vk), chunk_gated_delta_rule_fwd_opt_vk,
and the public chunk_gated_delta_rule_opt_vk.
Validated (op_tests/test_gated_delta_rule.py::test_chunk_opt_vk_state_indices):
the in-place-scatter path equals the dense path for the same logical state
across GVA / non-GVA, K==V / K!=V, fp16 / bf16, varlen with aligned and
unaligned chunk lengths; return_h has shape [B, NT, H, V, K]. Existing
test_chunk_opt_vk / _varlen are unchanged (20/20 pass).
Signed-off-by: hsthe29 <thehosy2002@gmail.com>
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
|
Hi @vgokhale @cagrikymk @azaidy 👋 This is my first contribution from a fork, so all CI workflows are currently sitting at Since this PR specifically backs SGLang's For context, everything is green on my side locally:
This pairs with #4057 (V-major decode kernel) - together they enable an end-to-end V-major Thanks a lot for taking a look! 🙏 |
| o: torch.Tensor | None = None, | ||
| num_decodes: int = 0, | ||
| num_decode_tokens: int = 0, | ||
| initial_state_indices: torch.Tensor | None = None, |
There was a problem hiding this comment.
Could you add description of the new args you added?
There was a problem hiding this comment.
Done — added descriptions for both new args (initial_state_indices and
return_h) to the chunk_gated_delta_rule_fwd_opt_vk docstring, and mirrored
them on the public chunk_gated_delta_rule_opt_vk and the
chunk_gated_delta_rule_fwd_h_opt_vk h-scan wrapper. Also documented the extra
h value returned when return_h=True. Pushed in 3079003 (docstring-only, no
functional change). Thanks for catching it!
Address review (@cagrikymk): the new initial_state_indices and return_h args on chunk_gated_delta_rule_fwd_opt_vk (and the public chunk_gated_delta_rule_opt_vk + the chunk_gated_delta_rule_fwd_h_opt_vk h-scan wrapper) were undocumented. Add their descriptions to the docstrings and note the extra `h` return when return_h=True. Docstring-only; no functional change. Signed-off-by: hsthe29 <thehosy2002@gmail.com>
|
Pushed 3079003 to address @cagrikymk's review - the new Still green locally on gfx950:
|
Summary
chunk_gated_delta_rule_opt_vkalready computes the gated-delta-rule chunk with a V-major[N, H, V, K]hidden state and GVA (Hgk-heads /Hv-heads) — but it could not back a paged / radix recurrent-state pool, for two reasons:initial_state([N, H, V, K]) and produced a separate densefinal_state. A pool keyed by per-request slot indices could not be read/written in place.h(produced internally by the VK h-scan), which a state-tracking cache needs to snapshot SSM state at chunk boundaries.Both are exactly what a serving framework's linear-attention state cache needs (e.g. SGLang's
MambaPool+ radix/extra-buffer tracking, whose own sgl-fla VK h-scan does precisely this).Change
Two new optional args; defaults preserve existing behavior byte-for-byte:
initial_state_indices— in-place scatter. When given, the h-scan reads the initial state from, and writes the final state back to, the same pool at slotinitial_state_indices[i_n]((index * H + i_h) * V * K) rather than a dense[N, …]buffer. Only the Triton VK path supports it (use_chunk_hip/use_chunk_flydslraise a clear error). Mirrors the sgl-fla VK h-scan (h0 == ht == pool + index*stride).return_h— expose the per-chunk snapshotsh[B, NT, H, V, K](already computed) for chunk-boundary state tracking.Threaded through: kernel
chunk_gated_delta_rule_fwd_kernel_h_opt_vk(+initial_state_indices, +USE_STATE_INDICESconstexpr) → wrapperchunk_gated_delta_rule_fwd_h_opt_vk→chunk_gated_delta_rule_fwd_opt_vk(+return_h) → publicchunk_gated_delta_rule_opt_vk.Correctness
New op_test
test_chunk_opt_vk_state_indices: the in-place-scatter path equals the trusted dense path for the same logical state, andreturn_hhas the documented shape. Coverage: GVA (H=3·Hg) + non-GVA,K==V+K!=V, fp16 + bf16, varlen with aligned and unaligned chunk lengths, non-identity slot permutation.o: indices(in-place) vs densehshape[B, NT, H, V, K]test_chunk_opt_vk/_varlen: 20/20 pass (dense path unchanged).black/ruffclean.mamba_v2_integration/task_5/test_aiter_chunk_vs_sglfla.py): vs SGLang's own sgl-flachunk_gated_delta_ruleon the same[V,K]pool + indices,o/h/ in-place final state all cos = 1.000000 (max abs err at bf16/fp16 noise, ~1e-4…4e-3), samehshape[1, NT, H, V, K].Benchmark (kernel, MI350X gfx950, GPU idle, min-of-N)
Qwen3.6-like GDN shapes (
Hg=16,H=48GVA×3,K=V=128, bf16), on the same[V,K]pool + indices.aiter_triton_idx= this PR's Triton VK path (indices + return_h);sglfla_idx= SGLang's own sgl-fla chunk (baseline it runs today). Reproducible over 2 runs.initial_state_indices+return_hadd no overhead (aiter_triton_idx≈ the denseaiter_hip), so the in-place scatter path is as fast as aiter's fastest dense path.Notes
initial_state_indices(raise). A follow-up can add it there.Related PR: #4057