Skip to content

triton attention: sync-free SWA metadata, per-shape launch-config table, split-KV policy (reorg 5/9) - #5

Open
mike-char wants to merge 5 commits into
cai-pr4-generic-featuresfrom
cai-pr5-triton-attn-tuning
Open

triton attention: sync-free SWA metadata, per-shape launch-config table, split-KV policy (reorg 5/9)#5
mike-char wants to merge 5 commits into
cai-pr4-generic-featuresfrom
cai-pr5-triton-attn-tuning

Conversation

@mike-char

Copy link
Copy Markdown

Part 5 of 9.

  • Sync-free sliding-window metadata: update_sliding_window_buffer no longer reads GPU scalars per step (two device-wide syncs that serialized step N+1 prep behind step N and collapsed overlap scheduling). Pure perf, all hybrid-SWA triton users.
  • Per-shape launch-config tuning table keyed by (kernel, head_dim, kv_group, kv_heads, bs/ntok bucket, ctx/prefix bucket); env-JSON > bundled > defaults that reproduce pre-existing launch values exactly (zero behavior change with no match; zero CUDA change). Measured tables ship separately.
  • Cost-model split-KV policy for few-KV-head decode shapes (SGLANG_G4_SPLITKV_POLICY, default off; measured winner scope=swa +6%).

Headline measured result (gemma-4 shapes, MI325X): waves_per_eu=0 beats the hardcoded 1 by 1.5-1.7x on the 512/GQA-8 decode shape. Upstream candidate: mechanism yes; needs GPU arch added to the table key first.

🤖 Generated with Claude Code

mike-char and others added 5 commits August 26, 2026 17:24
update_sliding_window_buffer read GPU scalars per step (torch.empty sized by
window_kv_indptr[-1] + a gpu-scalar slice bound), serializing step N+1's
metadata prep behind step N's kernels and collapsing the overlap schedule.
Thread the window totals from seq_lens_cpu / extend_prefix_lens_cpu instead;
hot paths never read GPU scalars.

(cherry picked from commit 864dca9fc, triton_backend portion)
Add SGLANG_G4_SPLITKV_POLICY (default OFF, byte-for-byte current behavior
when unset). When on, get_num_kv_splits sizes each request's splits from its
own KV length via a chunk chosen to fill ~3x the CU count with split
workgroups, and the per-request split cap is raised to 64 for shapes with
<=4 KV heads (Gemma-4 GLOBAL: 2 KV heads on 26b, 4 on 31b at head_dim 512).

max_kv_splits is raised once at init so attn_logits/attn_lse/cuda_graph_*
buffers and the kernel grid split-dim are all sized to the new cap at capture
time; the policy only writes values in [1, max_kv_splits] into the existing
num_kv_splits buffer, so no tensor shape or grid dim changes at graph replay.
Feeds both the triton grouped kernel and the g4 HIP shim unchanged. Deferred
to for MLA, deterministic, and fixed-split-tile modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit aa1115900d90553f5c0517489426d780de1daecd)
self.num_kv_head is the model's standard num_key_value_heads == the SWA-layer
count for hybrid Gemma-4 (8 on 26b, 16 on 31b), so keying the few-KV-head cap
raise on it alone missed the GLOBAL layers (2/4 KV heads) that are the whole
point -- per-layer KV-head count is only known at dispatch (k_buffer.shape[-2]).
Also raise the cap for any sliding-window model, which has a low-KV-head global
layer by construction. SWA layers share the mid-buffer but their window-capped
KV yields few splits, so the larger cap costs only buffer bytes, not wrong math.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit e039b2f8ec4a624a39624d30872ee7e00d164883)
Measured (MI325X): the cost-model split policy REGRESSES the full/global shape
(stage-2 combine + partial-output traffic outweigh the occupancy gain) and WINS
~6% on the window-capped SWA shape. Make the policy tunable without editing the
file so the winning regime can be found by sweep:

- SGLANG_G4_SPLITKV_CU_MULT (default 3): target split-workgroups per CU.
- SGLANG_G4_SPLITKV_MIN_BLOCK (default 32): min/rounding tile for the chunk.
- SGLANG_G4_SPLITKV_MAX_SPLITS_FEWKV (default 64): raised few-KV-head cap.
- SGLANG_G4_SPLITKV_SCOPE = all|swa|full (default all): which decode shapes the
  per-request policy applies to. "swa" is the measured winner.

Knobs are resolved once into _SplitKVConfig at init and logged (scope + values +
resolved max_kv_splits) so bench logs are self-describing. Scope is decided at
the policy writer from an is_window flag threaded from the two window call sites
(the cleanest signal there: full seq_len vs window-capped length). swa scope
also skips the few-KV-head cap raise, since window-capped KV never exceeds the
stock cap and the larger mid-buffer would be pure waste.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit fd1869d861ce60e5644520bb79e8a13815756b41)
Replace hardcoded launch constants in the grouped decode (stage1/stage2) and
extend kernels with a bucketed lookup keyed by
(kernel, head_dim, kv_group, kv_heads, bs/ntok bucket, ctx/prefix bucket).
Resolution: env JSON (SGLANG_TRITON_ATTN_TUNING, sweep injection) > bundled
table > hardware defaults that reproduce exactly the values the kernels
launched with before this module existed — zero behavior change when no
entry matches, and zero CUDA change. Measured tables ship separately as
optional gemma4_mi325x_tuning.py (guarded import).

Squashed final state of cab84c2a8 + 42d209ada + the tuning_table portions of
c4cf5ecca/219af68f4 (original history on archive branches).
@mike-char
mike-char force-pushed the cai-pr4-generic-features branch from 98438b9 to 2e2f367 Compare August 27, 2026 00:27
@mike-char
mike-char force-pushed the cai-pr5-triton-attn-tuning branch from 364e93e to 25b01af Compare August 27, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant