Skip to content

[Triton/Gluon] Gfx1250 flash kda gluon - #6609

Merged
azaidy merged 7 commits into
mainfrom
gfx1250-flash-kda-gluon
Oct 26, 2026
Merged

azaidy merged 7 commits into
mainfrom
gfx1250-flash-kda-gluon

Conversation

@Liang-jianhao97

@Liang-jianhao97 Liang-jianhao97 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

On gfx1250, FlashKDA (flash_kda_fwd, invoked by chunk_kimi_delta_attn) uses pointer‑based tl.load for loads, which Triton does not lower to TDM. Furthermore, its performance is worse than MI355, which is unexpected.

Technical Details

gfx1250 Gluon K1 and K2

  • aiter/ops/triton/_gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k1.py: the prepare kernel. q, k and g arrive by one fused TDM load, and the [C, C] products and the (I - L)^-1 solve run on wave32 WMMA. Same workspace contract and gluon_k1_prepare entry point as the gfx950 K1, plus the token strides, num_warps and waves_per_eu.
  • aiter/ops/triton/_gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k2.py: the chunk walk. Pass A computes b_seg and A_seg in one launch, where Triton takes two; pass C computes the outputs and the final state, paged state_cache included. The state stays transposed in a WMMA accumulator, the workspace tiles and v arrive by fused TDM loads into a double buffer, and the outputs leave LDS by async copy.
  • aiter/ops/triton/_gluon_kernels/common/utils.py: sigmoid_log2, fast_sigmoid, sum_combine and l2norm, which the gfx1250 K1 and K2 import. The sigmoid already in this file uses tanh and gives different results.
  • aiter/ops/triton/_triton_kernels/kimi_delta_attn/flash_kda.py: on gfx1250, K1 and both K2 passes go to Gluon. AITER_FDA_USE_GLUON now applies on gfx1250 too: 1, the default, routes K1 and K2 to Gluon, k1 or k2 routes only that one, and 0 keeps FlashKDA on Triton. Routing on gfx950 is unchanged; pass C stays on Triton there. _k1_gluon_schedule reads K1's num_warps and waves_per_eu from the published config. _k2_gluon_schedule takes the pass name and returns its whole entry, so each pass has its own wide and narrow entries, CPL included.
  • aiter/ops/triton/configs/gfx1250/gluon/attention/chunk_delta_attn/DEFAULT.json: K1 at four warps and waves_per_eu 5. Each K2 pass has BW 128 (wide) and BW 64 (narrow) at four warps, with CPL 2, the number of chunks in one TDM load. A pass runs wide when that still gives at least 2 blocks per CU for pass A, or 0.3 for pass C; both floors were calibrated on the bench_chunk_kda.py and bench_flash_kda.py shapes.

Pass B on gfx1250

  • flash_kda.py: pass B, the cross-segment scan, stays on Triton. It is a chain of dependent updates, and _flash_kda_seg_scan_kernel loads b_seg in the update that adds it: two exposed memory round trips per update. On gfx1250, pass B runs _flash_kda_seg_scan_prefetch_kernel at BV=16 and four warps. It does two updates per loop trip and loads the next trip's b_seg two to three updates ahead, while the software pipeliner prefetches A_seg. It also skips the last segment's update, which nothing reads. h_in matches the old kernel to 2.4e-7 with dense, no and paged initial states. AITER_FDA_SCAN_PREFETCH=0 goes back to the old kernel; other archs keep the old kernel and its schedule.

Strided q, k, v and g, read in place

  • The gfx1250 K1 reads q, k and g, and both K2 passes read v, each at its own token stride, which TDM descriptors take at run time. The output stays packed.
  • flash_kda.py: q, k, v and g are left out of input_guard. A view that a gfx1250 Gluon kernel reads goes in as it is when the descriptor can walk it (the heads of each token packed, and B == 1); anything else is packed as before. Packed inputs only pay for the contiguity checks.
  • aiter/ops/triton/kimi_delta_attn/chunk_delta_attn.py, aiter/ops/triton/_triton_kernels/kimi_delta_attn/chunk_fwd.py: the public wrapper no longer packs q, k, v and g ahead of chunk_delta_attn_fwd, which packs them after the FlashKDA branch for the kernels below it.
  • TDM needs no alignment beyond the element's: a base 2 bytes off 16-byte alignment and an odd token stride both read bit-exact, so no alignment condition is added.

Benchmark

  • op_tests/op_benchmarks/triton/bench_flash_kda.py: --k3-tp8 runs the 26 prefill forwards of Kimi-K3 TP8+DCP8 rank 0. Each forward is one varlen call, and a sequence that continues a cached prefix reads its initial state from a paged state cache. --qkv-bands passes q, k and v as bands of one fused projection, as the model does. --impl chunk_kda times the Gluon chunk_kda on the same inputs, and --shape can repeat. The default sweep and its inputs do not change.

Tests

  • op_tests/triton_tests/kimi_delta_attn/test_flash_kda.py: the schedule and routing tests also cover pass C on archs where it has a Gluon kernel, and read BW and num_warps from the returned config.
  • op_tests/triton_tests/kimi_delta_attn/test_fast_launch.py: the fast-launch tests take the Gluon kernels of the device's arch, pass C on gfx1250 included, and the prefetching pass B kernel.

Test Plan

  • gfx1250 (A0 stepping, 256 CUs), one GPU (HIP_VISIBLE_DEVICES=0). Image rocm/fw-bringup-mlperf:gfx1250-atom-20261008: ROCm SDK 10.1.0a20260811, PyTorch 2.11.0, Triton 3.8.0. Upstream is main at 8779c30, the merge base of this PR.
  • op_tests/triton_tests/kimi_delta_attn/test_flash_kda.py and test_fast_launch.py on gfx1250, with AITER_FDA_SCAN_PREFETCH=1 (the default) and 0.
  • op_tests/op_benchmarks/triton/bench_chunk_kda.py (--timing do_bench and --timing cudagraph) and bench_flash_kda.py, upstream and this PR interleaved in one session. Upstream runs this PR's bench_flash_kda.py, as --k3-tp8, --qkv-bands and --impl are new. Each result gives its command and its number of rounds; times are means over the rounds. No AITER_FDA_* variable is set unless a result says so.

Test Result

  • test_flash_kda.py and test_fast_launch.py: 107 passed, 2 skipped, with and without the pass B prefetch (gfx1250 has no Triton K2 autotune candidates to switch between; one test needs a second GPU).
  • Errors against the fp32 reference are unchanged: o 3.1e-3 and state 2.4e-3 on the K3 cases, as with upstream FlashKDA (chunk_kda Gluon: 4.5e-3 and 3.3e-3).

bench_chunk_kda.py --timing do_bench, us, two rounds. H=24 and H=12 are Kimi-K3's 96 heads sharded TP4 and TP8. The script's triton line calls chunk_kimi_delta_attn, which runs FlashKDA (flash_kda_fwd in flash_kda.py); its gluon line runs chunk_kda. On this PR the triton line runs the Gluon K1 and K2 by default, which is This PR, and AITER_FDA_USE_GLUON=0 AITER_FDA_SCAN_PREFETCH=0 puts it back on upstream's Triton kernels and schedules, which is upstream (triton). The gluon line reads neither variable and chunk_kda is the same code on both trees, so it is upstream (chunk_kda gluon) in either run. Run from the root of this PR's checkout; PYTHONPATH=$PWD keeps an installed aiter from shadowing it.

# This PR (the triton line) and upstream (chunk_kda gluon) (the gluon line)
HIP_VISIBLE_DEVICES=0 python op_tests/op_benchmarks/triton/bench_chunk_kda.py --timing do_bench
# upstream (triton): the triton line on upstream's Triton kernels
HIP_VISIBLE_DEVICES=0 AITER_FDA_USE_GLUON=0 AITER_FDA_SCAN_PREFETCH=0 python op_tests/op_benchmarks/triton/bench_chunk_kda.py --timing do_bench

The upstream columns were measured on main at 8779c30. On three of the shapes, one of them segmented, this PR with both variables off launches the same kernels as main, with the same grids and blocks, and gives bit-identical outputs. Over four interleaved rounds on a second GPU of the same machine, its triton line is 0.90-1.10x of main's per shape, geomean 0.99x; main's own rounds differ by up to 39% at a shape, as its Triton K2 autotunes in each process.

B T H upstream (triton) (us) upstream (chunk_kda gluon) (us) This PR (us)
1 1024 12 200.9 87.3 62.3
1 1024 24 182.6 88.3 62.4
1 4096 12 416.7 279.0 97.4
1 4096 24 486.3 303.6 108.2
3 1024 12 198.0 90.1 67.1
3 1024 24 201.9 89.5 65.4
3 4096 12 521.8 218.5 127.3
3 4096 24 465.8 247.4 167.5
8 1024 12 203.6 91.6 69.2
8 1024 24 331.6 136.0 100.7
8 4096 12 531.0 289.6 204.2
8 4096 24 938.6 491.3 348.8

This PR is 2.60-4.49x faster than upstream (triton), geomean 3.23x, and 1.32-2.87x faster than upstream (chunk_kda gluon), geomean 1.59x. With --timing cudagraph (one round on upstream, two on this PR): 2.46-5.70x and 1.25-3.55x.

bench_flash_kda.py --k3-tp8, us, four rounds; the chunk_kda gluon column is --impl chunk_kda on upstream. These are the 26 prefill forwards of Kimi-K3 TP8+DCP8 rank 0 (H=12) in order, and step 13 is the headline case. seqlens lists the sequences of each forward; only the sequences that continue a cached prefix start from a carried state.

step seqlens upstream (triton) (us) upstream (chunk_kda gluon) (us) This PR (us)
0 12934 435.6 850.2 118.2
1 13295+3072 643.0 888.1 136.1
2 9170+7168 598.9 649.1 135.1
3 6740+9600 529.2 672.0 135.1
4 2370+11585+2368 633.2 498.1 136.1
5 9315+7040 641.1 656.4 135.8
6 5872+10496 612.3 725.3 135.5
7 1019+13074+2240 559.1 545.8 138.4
8 11321+5056 610.9 772.2 137.1
9 6419+9920 529.2 690.7 135.8
10 2431+13801+128 612.1 568.9 137.2
11 13958+2368 544.0 934.2 137.4
12 10840+5504 537.5 749.7 136.2
13 8749+7616 592.4 621.3 135.9
14 5665+10688 578.0 734.5 136.7
15 2872+13331+128 597.5 547.7 136.6
16 12899+3456 573.0 865.0 136.8
17 10694+5632 538.2 736.0 136.2
18 6631+9728 560.4 678.8 135.4
19 4080+12288 561.9 826.5 136.7
20 1104+13662+1600 579.9 561.0 137.1
21 9964+6400 561.6 693.5 135.7
22 6658+9664 527.3 674.6 135.2
23 4263+12096 555.6 817.6 136.6
24 502+11971+3904 555.7 505.1 136.1
25 9992 418.9 660.6 98.7

This PR is 3.69-4.72x faster than upstream (triton), geomean 4.20x, and 3.66-7.19x faster than upstream (chunk_kda gluon), geomean 5.13x. Upstream's Triton K2 autotunes in each process, so its times move by up to 16% between rounds; this PR's move by less than 5%.

bench_flash_kda.py --qkv-bands --shape 1 4096 96 128 128 --shape 1 13312 96 128 128 --shape 1 16384 96 128 128, ms, four rounds; the chunk_kda gluon column is --impl chunk_kda on upstream. H=96, with q, k and v as bands of one fused projection.

B T H upstream (triton) (ms) upstream (chunk_kda gluon) (ms) This PR (ms)
1 4096 96 0.587 0.285 0.196
1 13312 96 1.759 0.912 0.629
1 16384 96 2.169 1.126 0.780

This PR is 2.78-3.00x faster than upstream (triton) and 1.44-1.46x faster than upstream (chunk_kda gluon). Upstream packs the bands before its kernels, which costs 0.14-0.45 ms per call over packed inputs (the same command without --qkv-bands; one round on upstream, two on this PR). This PR reads the bands in place and stays within 3 us of packed inputs.

Pass B's prefetch, the two commands above with AITER_FDA_SCAN_PREFETCH=0 against the default, two rounds: the TP8 forwards are 1.06-1.22x faster with it, geomean 1.09x; H=96 does not change (0.997-1.001x).

bench_flash_kda.py (default sweep; one round on upstream, two on this PR): 2.13-5.65x over upstream on 12 of its 13 shapes. B=1, T=256, H=12 is host-bound: a call takes about 44 us on both trees (0.98x). The in-place reads add about 0.9 us of host time per call (four more kernel arguments); GPU time on packed inputs is unchanged.

Submission Checklist

@Liang-jianhao97
Liang-jianhao97 requested a review from a team October 16, 2026 04:07
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:gfx1250-ffm-triton Run the five-shard gfx1250 FFM Triton test suite
ci:triton-300x Run an additional Triton test job on MI300X in PRs (added automatically when gfx942 configs change); main branch always runs both MI35X and MI300X
ci:triton-355 Run the full Triton test suite on MI35X, not only the tests the change affects
ci:windows Windows JIT op smoke test on the gfx1201 ROCm runner
ci:multi-gpu Aiter multi-GPU tests on the 8-GPU runner
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: DeepSeek-V4-Pro, MiniMax-M3-MXFP4
ci:all All standard extended tests (excludes ci:atom_full)

Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 6609 --add-label <label>

One backend per PR:
A PR changes one kernel backend: [Triton/Gluon] (Triton and Gluon count as one), [HIP], [ASM], [CK], [OPUS] or [FlyDSL]. If the title ends up with two backend tags, split the PR -- as stacked pull requests when one part cannot merge without the other.

PR title tags & labels:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title and as PR labels automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf], op tags like [MLA], and human labels (ci:*) are left untouched. Add the no-auto-title label to stop the title rewrites; labels stay in sync either way.

@github-actions github-actions Bot changed the title Gfx1250 flash kda gluon [Triton/Gluon] Gfx1250 flash kda gluon Oct 16, 2026
@Boss2002n
Boss2002n requested a balanced review from Copilot October 16, 2026 05:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Positional calls still pack projection views, and new fallback tuning bypasses the required JSON configuration.

2 open findings
What changed in this PR

Ports FlashKDA to gfx1250 Gluon kernels, adding architecture-specific acceleration within the existing Kimi attention pipeline.

Changes:

  • Adds TDM loads, wave32 WMMA computation, and per-pass K2 schedules.
  • Enables supported strided inputs without packing.
  • Extends layout, routing, and launch-cache tests.
File Description
op_tests/​triton_tests/​kimi_delta_attn/​test_flash_kda.py Tests routing, schedules, and strided inputs.
op_tests/​triton_tests/​kimi_delta_attn/​test_fast_launch.py Selects architecture-specific launchers.
aiter/​ops/​triton/​kimi_delta_attn/​chunk_delta_attn.py Defers operand packing to pipeline dispatch.
aiter/​ops/​triton/​configs/​gfx1250/​gluon/​attention/​chunk_delta_attn/​DEFAULT.json Publishes K2 schedules.
aiter/​ops/​triton/​_triton_kernels/​kimi_delta_attn/​flash_kda.py Adds gfx1250 dispatch and stride handling.
aiter/​ops/​triton/​_triton_kernels/​kimi_delta_attn/​chunk_fwd.py Preserves packing for the default pipeline.
aiter/​ops/​triton/​_gluon_kernels/​gfx1250/​kimi_delta_attn/​flash_kda_k2.py Implements chunk recurrence and output passes.
aiter/​ops/​triton/​_gluon_kernels/​gfx1250/​kimi_delta_attn/​flash_kda_k1.py Implements workspace preparation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@amd-ruitang3
amd-ruitang3 force-pushed the gfx1250-flash-kda-gluon branch 2 times, most recently from 1ab7278 to de7eff6 Compare October 16, 2026 14:11
@Liang-jianhao97
Liang-jianhao97 force-pushed the gfx1250-flash-kda-gluon branch 2 times, most recently from c9482a9 to 8865724 Compare October 18, 2026 12:39
Port the Gluon FlashKDA kernels to gfx1250, in the gfx950 layout, and route
flash_kda_fwd to them there.

- _gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k1.py: the prepare kernel.
  q, k and g arrive by one fused TDM load, and the [C, C] products and the
  (I - L)^-1 solve run on wave32 WMMA. Same workspace contract and the same
  gluon_k1_prepare entry point as the gfx950 K1.
- _gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k2.py: the chunk walk.
  Pass A computes b_seg and A_seg in one launch; pass C computes the outputs
  and the final state, paged state_cache included. The state stays transposed
  in a WMMA accumulator, the workspace tiles and v arrive by fused TDM loads
  into a double buffer, and the outputs leave LDS by async copy.
- flash_kda.py: on gfx1250, K1 and both K2 passes go to Gluon. Routing on
  gfx950 is unchanged; pass C stays on Triton there. _k2_gluon_schedule now
  takes the pass name, so each pass has its own wide and narrow entries.
- configs/gfx1250/gluon/attention/chunk_delta_attn/DEFAULT.json: BW 128 and
  BW 64 at four warps. The wide floors were calibrated on the bench_chunk_kda
  and bench_flash_kda shapes: 2 blocks per CU for pass A, 0.3 for pass C.
- test_flash_kda.py: the schedule and routing tests also cover pass C on
  archs where it has a Gluon kernel, and import that arch's K2 module.

Tests on gfx1250: op_tests/triton_tests/kimi_delta_attn/test_flash_kda.py,
76 passed, 1 skipped (this device has no K2 autotune candidates to switch
between).

Performance on gfx1250 (one GPU, 256 CUs), upstream 428af1c -> this commit,
both runs with the same environment.

bench_chunk_kda.py, the FlashKDA line ("triton"), us:

   H  B     T        do_bench             cudagraph
  24  1  1024   178.8 ->  62.3 2.87x   176.9 ->  37.1 4.77x
  24  1  4096   486.0 ->  99.7 4.87x   487.9 ->  97.8 4.99x
  24  3  1024   190.8 ->  67.0 2.85x   184.8 ->  54.6 3.38x
  24  3  4096   448.1 -> 170.1 2.63x   449.7 -> 168.7 2.67x
  24  8  1024   295.1 -> 102.7 2.87x   302.2 -> 101.1 2.99x
  24  8  4096   925.2 -> 340.5 2.72x   924.1 -> 343.2 2.69x
  12  1  1024   171.4 ->  61.9 2.77x   199.4 ->  34.6 5.76x
  12  1  4096   410.8 -> 106.0 3.87x   398.5 ->  98.7 4.04x
  12  3  1024   220.4 ->  65.9 3.34x   170.5 ->  40.9 4.17x
  12  3  4096   561.2 -> 126.1 4.45x   546.9 -> 134.1 4.08x
  12  8  1024   207.6 ->  69.8 2.97x   205.6 ->  65.1 3.16x
  12  8  4096   542.5 -> 205.7 2.64x   534.6 -> 207.3 2.58x

The FlashKDA line is now faster than the chunk_kda Gluon line ("gluon") on
every shape in both timings. Upstream, it was slower on every shape.

bench_flash_kda.py, ms:

  B      T   H   upstream   this    speedup
  1    256  12    0.0479   0.0417   1.15x
  1    512  12    0.0753   0.0396   1.90x
  1   1664  12    0.1119   0.0418   2.68x
  1   2048  12    0.3429   0.0700   4.90x
  1   8192  12    0.4866   0.1050   4.63x
  1  16384  12    0.6712   0.1489   4.51x
  1  16384  64    2.0335   0.5721   3.55x
  1  16384  96    1.7003   0.7673   2.22x
  1   8192  32    0.7352   0.1615   4.55x
  1   8192  64    1.2016   0.3074   3.91x
  2   2664  12    0.3962   0.0839   4.72x
  4   4096  16    0.7366   0.1587   4.64x
  8   2048  16    0.3784   0.1278   2.96x

Tests on gfx1250: test_flash_kda.py and test_fast_launch.py, 107 passed,
2 skipped; test_chunk_delta_attn_fwd.py, 39 passed;
@Liang-jianhao97
Liang-jianhao97 force-pushed the gfx1250-flash-kda-gluon branch from 8865724 to b1b335d Compare October 18, 2026 12:47
@zufayu
zufayu requested review from a team and azaidy October 18, 2026 23:32
@azaidy azaidy assigned omuhamma and unassigned azaidy Oct 19, 2026
azaidy
azaidy previously requested changes Oct 22, 2026

@azaidy azaidy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please look at copilot and @omuhamma comments

jianlian added 2 commits October 23, 2026 12:20
…JSON

Address the review of the gfx1250 Gluon FlashKDA kernels: share the Gluon
helpers, and read the K1 and K2 launch values from the published config.

- _gluon_kernels/common/utils.py: sigmoid_log2, fast_sigmoid, sum_combine
  and l2norm, in the form that the FlashKDA kernels used. The sigmoid that
  is already in this file uses tanh and gives different results.
- _gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k1.py: import these
  helpers and call gl.exp2 directly. gluon_k1_prepare takes num_warps and
  waves_per_eu; the kernel still requires four warps.
- _gluon_kernels/gfx1250/kimi_delta_attn/flash_kda_k2.py: import
  fast_sigmoid. Both launchers take CPL, the number of chunks in one TDM
  load, in place of the module constant _CPL.
- flash_kda.py: _k1_gluon_schedule reads the k1_prepare_gluon entry, and
  _k2_gluon_schedule returns the whole entry, so pass A and pass C also get
  CPL. The fallbacks do not depend on the arch.
- configs/gfx1250/gluon/attention/chunk_delta_attn/DEFAULT.json: a
  k1_prepare_gluon entry with 4 warps and waves_per_eu 5, and CPL 2 in the
  four K2 entries. These are the values that were in Python.
- test_flash_kda.py: read BW and num_warps from the returned config.

The outputs, the final states and the ISA of all K1 and K2 variants do not
change.

Tests on gfx1250: test_flash_kda.py and test_fast_launch.py, 108 passed,
1 skipped (this device has no K2 autotune candidates to switch between).
Pass B, the cross-segment scan, is a chain of dependent updates, and
_flash_kda_seg_scan_kernel loads b_seg in the update that adds it: two
exposed memory round trips per update. _flash_kda_seg_scan_prefetch_kernel
does two updates per loop trip and loads the next trip's b_seg, two to three
updates ahead, while the software pipeliner prefetches A_seg. It also skips
the last segment's update, which nothing reads.

It is pass B on gfx1250 (AITER_FDA_SCAN_PREFETCH=0 goes back to the old
kernel) at BV=16, 4 warps and the default 2 stages; other archs keep the old
kernel and its schedule.

On H=12 forwards of 1 to 3 sequences, 13 to 20 segments deep, pass B is
1.8-2.5x faster and the forward's kernel time 6-17% lower. h_in matches the
old kernel to 2.4e-7 with dense, no and paged initial states.
omuhamma
omuhamma previously approved these changes Oct 23, 2026

@omuhamma omuhamma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

@omuhamma omuhamma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please add a benchmark file in op_tests/op_benchmarks so others can repro your results

@omuhamma
omuhamma dismissed their stale review October 23, 2026 16:24

benchmark file needed

Micky774 added a commit that referenced this pull request Oct 23, 2026
…per with #6609

- Move sigmoid_log2/fast_sigmoid/sum_combine/l2norm into
  _gluon_kernels/common/utils.py (same definitions as #6609); k1 uses them.
- Align flash_kda.py with #6609: _k2_gluon_schedule(..., kernel) returns a
  Config, use_gluon_k2a/k2c split, _GLUON_K2C_ARCH_OK, unpaged_args; gfx950
  k2 gets gluon_k2_ab_fused/gluon_k2_c launchers mirroring gfx1250's.
- Tests: arch-generic Gluon module imports, pass-C checks gated on arch.
- Fix remap_xcd import path (moved to _triton_kernels/common in #6505).
- Trim verbose comments.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Time FlashKDA on the calls that Kimi-K3 makes, and chunk_kda on the same
inputs.

- --k3-tp8: the 26 prefill forwards of Kimi-K3 TP8+DCP8 rank 0 (H=12), in
  order. Each forward is one varlen call. A sequence that continues a cached
  prefix reads its initial state from a paged state cache; the others start
  from zero. The table and the CSV gain a seqlens column.
- --qkv-bands: pass q, k and v as views of one fused [B, T, 3 * H * 128]
  projection, as the model does, instead of three packed tensors.
- --impl chunk_kda: time the Gluon chunk_kda on the inputs that FlashKDA
  gets; a batch becomes one varlen call. --seg-sweep needs --impl flash_kda.
- --shape can repeat. It and --k3-tp8 exclude each other.

The default sweep and its inputs do not change.

Run on gfx1250 with --k3-tp8, --k3-tp8 --impl chunk_kda, --qkv-bands with
repeated --shape, and the default sweep.
@Liang-jianhao97

Liang-jianhao97 commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Please add a benchmark file in op_tests/op_benchmarks so others can repro your results

@omuhamma Added in 5cf2e0f. op_tests/op_benchmarks/triton/bench_flash_kda.py now takes --k3-tp8 (the 26 prefill forwards of Kimi-K3 TP8+DCP8 rank 0), --qkv-bands (q, k and v as bands of one fused projection) and --impl chunk_kda (chunk_kda on the same inputs). Every timing in the description now comes from bench_chunk_kda.py or bench_flash_kda.py, and each table gives its command.

jianlian added 2 commits October 26, 2026 03:08
bench_flash_kda.py conflicted: main builds the parser with get_parser
(#6347), next to the imports and the --shape/--k3-tp8 group this branch
added. Keep both.
get_num_xcds (#6327) now imports aiter.jit.utils.chip_info on every call.
Under AITER_TRITON_ONLY=1, which bench_flash_kda.py sets, that import fails
and is retried each time, about 2 ms. Pass A and pass C call it on every
launch, so after the merge of main bench_flash_kda.py read 2-4 ms for every
FlashKDA call. Cache the count, as build_layouts already is.

@omuhamma omuhamma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm now

@azaidy
azaidy dismissed their stale review October 26, 2026 16:31

Resolved issues

@azaidy
azaidy merged commit ffddbaf into main Oct 26, 2026
70 checks passed
@azaidy
azaidy deleted the gfx1250-flash-kda-gluon branch October 26, 2026 20:32
eppaneamd added a commit that referenced this pull request Oct 27, 2026
…ens-cpu

Conflicts with #6609 (gfx1250 FlashKDA, ffddbaf), resolved by keeping
both sides:

- chunk_fwd.py: main packs q, k, v and g after the FlashKDA return; the
  default pipeline then builds chunk_indices from cu_seqlens_cpu.
- chunk_delta_attn.py: cu_seqlens_cpu is accepted (no longer listed as
  rejected); main's contiguity note, with the gfx1250 exception, is kept.
- test_flash_kda.py: both test blocks kept. #6609's
  test_positional_call_keeps_skipped_storage lists every flash_kda_fwd
  parameter, so it gains cu_seqlens_cpu.
- bench_flash_kda.py: --varlen's column is named "bounds", since
  --k3-tp8's "seqlens" column holds the lengths. --varlen rejects --k3-tp8
  (its inputs carry their own cu_seqlens) and --impl chunk_kda (it would
  time flash_kda_fwd).

The gfx1250 K1 and K2 read chunk_indices and the segment descriptors as
the gfx950 kernels do, so they take the int32 metadata from
_build_metadata unchanged.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Rony Leppänen <rony.leppanen@amd.com>
azaidy pushed a commit that referenced this pull request Oct 27, 2026
…on gfx950 (#6177)

* [Triton/Gluon] FlashKDA K2: keep a (segment, head)'s V blocks on one XCD

The K2 grid is (cdiv(W, BW), num_segs * H), and every V block of one
(segment, head) re-reads that segment's whole chunk workspace. Workgroups
reach the XCDs round-robin in launch order, x fastest, so with the tuned
BW=16 the eight V blocks of each (segment, head) landed on eight different
XCDs and each pulled the workspace through its own L2.

Measured on MI355X (rocprofv3 FETCH_SIZE/WRITE_SIZE): passes A and C moved
6-8x their compulsory bytes past L2, pinned at ~7 TB/s on every shape --
they were bound by L2-miss traffic, not by the recurrence. Renumbering the
flat program id with the existing remap_xcd, so each XCD owns a contiguous
run of whole (segment, head) groups, brings that traffic to 1.01-1.02x
compulsory and halves those kernels' time at an unchanged grid and tile.

Applied to the Triton segment kernel (passes A and C) and the Gluon pass A,
whose BW=32/64 tiles split V the same way. The remap only changes which
program computes which tile, so results are bit-identical.

FlashKDA end to end, MI355X, bf16, K=V=128:
  gfx950 default route (Gluon K1 + pass A, Triton pass C): 1.24-1.45x
  Triton only (non-gfx950 route):                          1.32-1.61x
  1x512x12:                                                1.01x

* [Triton/Gluon] KDA: load token i once in intra_token_parallel

The token-parallel intra kernel (the default pipeline's path when safe_gate
is off) reloaded token i's q/k/g for every j and every K block. Load them
once outside the j loop, span all of K in one block (BK from K, no longer
tuned) and fold beta into k, following fla. The default config moves from
BH=1 / 4 warps, which idles most of each warp, to BH=4 / 1 warp: within
1.24x of the per-shape best on gfx950.

Default pipeline forward with safe_gate=False on MI355X: 2.0-2.7x
(1x8192x32 2862 -> 1221 us). Aqk/Akk change only in summation order
(output within 2.2e-5 relative).

* [Triton/Gluon] FlashKDA: cap segment length at 64 chunks

_choose_chunks_per_seg capped the segment count at 16 over both of its
reasons, so a long few-head sequence got 256-chunk segments whose serial
walk bounds the recurrence. Cap the occupancy target alone at 16 segments
and then cut segments to at most 64 chunks however many that takes. On
gfx950, 64-chunk segments beat longer ones by up to 1.9x on 1x131072x4.

* [Triton/Gluon] FlashKDA: LDS-prefetching Gluon K2 and a Gluon pass C

Speeds up the Gluon FlashKDA kernels on gfx950 and moves pass C to Gluon:

- K1: sigmoids through exp2 (one v_exp_f32 instead of exp's
  denormal-safe sequence); the gate in a column-per-thread layout, so its
  cumulative sum is in-thread, with tail rows given a zero gate (the last
  and pivot rows are then fixed rows); tail-row masks on the workspace
  dropped; warps split the rows of the [C, K] tiles, so the l2norm sums stay
  in a warp; operands staged through LDS once per product (10 barriers
  instead of 38); the gate load is masked, since buffer loads past the
  tensor's end are not clamped. Launches with waves_per_eu=3 (170 -> 168
  VGPRs, 3 waves/SIMD).
- K1 (Gluon and Triton) writes sigmoid(beta) to a new ws_beta workspace,
  which K2 copies into LDS with the chunk's other tiles.
- Pass A: chunk j+1's tiles are async-copied into a double-buffered LDS
  stage while chunk j computes; one barrier per chunk. The kernel needs
  ~280 VGPRs, so the published schedules carry waves_per_eu=2.
- Pass C: new k2_c_gluon, used for every shape on gfx950. Same prefetch;
  the output projection runs before the state update and is stored from
  the MFMA layout; seg_seq / seg_is_last are read before the loop. It
  supports the paged state_cache read and write-back of the Triton pass C.
  BW=64 unsegmented, BW=128 segmented.

FlashKDA forward on MI355X vs main: 1.43-3.02x (1x8192x12 279 -> 142 us,
1x8192x96 1458 -> 690 us, 1x131072x4 1634 -> 541 us, varlen 1.70-1.99x),
including the two commits before this one. Output within 4.5e-4 relative
of main (K1's l2norm and gate summation order), final state within 3.5e-5.

Tests: test_cases_reach_the_gluon_k2 now checks pass C as well as pass A,
the paged-cache test runs on both K2 routes, and the schedule test covers
the pass C tiles.

* [Triton/Gluon] FlashKDA: skip ws_beta off the Gluon K2 route; name K2 kernels by config

- ws_beta is only read by the Gluon K2, so it is now allocated only when
  K2 takes that route, and both K1s store it only then (STORE_BETA). The
  K2 route is resolved before K1 launches for this.
- The Gluon pass A and pass C names now carry their warp count, through a
  NUM_WARPS constexpr that must equal the launch's num_warps (num_warps
  itself is not a constexpr and would name as NONE), and pass C's also
  carries STATE_V_FIRST.
- test_fast_launch covers the pass C launcher too.

* [Triton/Gluon] FlashKDA: keep small unsegmented pass C on Triton; bench without safe_gate

- On unsegmented shapes with few (sequence, head) pairs, the serial walk
  over the chunks sets pass C's time, and the Triton kernel's lighter
  iteration at BW=16 beats the Gluon one, which cannot go narrower than
  BW=32: 1.08-1.18x for Triton at <= 32 pairs on MI355X, while Gluon wins
  from 40 pairs up (to 2.2x). The gfx950 k2_c_gluon schedule now publishes
  a MIN_BLOCKS_PER_CU floor, as pass A's does, below which pass C runs on
  Triton; ws_beta is then skipped too. Segmented shapes keep the Gluon
  pass C.
- bench_chunk_delta_attn.py gains --no-safe-gate, which takes the
  token-parallel intra kernel.

bench_flash_kda.py default sweep vs main on MI355X: 1.04-2.61x (1x256x12
and 1x1664x12 were 0.91x and 0.94x before this change).

* [Triton/Gluon] FlashKDA: publish the Gluon K1 occupancy hint in the gfx950 config

K1's waves_per_eu=3 was pinned in Python while pass A's occupancy hints are
published in configs/gfx950/gluon/.../chunk_delta_attn/DEFAULT.json. Move it
there (k1_prepare_gluon) and load it through chunk_delta_attn_tuned_config,
so retuning it is a config change. The value holds K1 to ~168 VGPRs, i.e. 3
waves per SIMD; left alone it lands at 170 and drops to 2 (0.85x).

The warp count stays fixed by K1's layouts: the published entry restates it,
and an entry naming another count is rejected rather than launched against
them. Devices without an entry get no hint. Compiled K1 on gfx950 is
unchanged (waves_per_eu=3, 164 VGPRs).

* [Triton/Gluon] KDA: address review — shared Gluon helpers, align wrapper with #6609

- Move sigmoid_log2/fast_sigmoid/sum_combine/l2norm into
  _gluon_kernels/common/utils.py (same definitions as #6609); k1 uses them.
- Align flash_kda.py with #6609: _k2_gluon_schedule(..., kernel) returns a
  Config, use_gluon_k2a/k2c split, _GLUON_K2C_ARCH_OK, unpaged_args; gfx950
  k2 gets gluon_k2_ab_fused/gluon_k2_c launchers mirroring gfx1250's.
- Tests: arch-generic Gluon module imports, pass-C checks gated on arch.
- Fix remap_xcd import path (moved to _triton_kernels/common in #6505).
- Trim verbose comments.

* [Triton/Gluon] KDA: address review — published schedules, resolved-route log, launch-level route tests

- K1 and pass C read their Gluon schedules through _published_gluon_schedule
  and fall back to Triton when none is published (no Python fallback). The
  K1 schedule moves to the wrapper; gluon_k1_prepare rejects a wrong warp count.
- Segment cap moves to the gfx950 Triton config (flash_kda_segments:
  MAX_CHUNKS=64, HARD_CAP). Other archs keep the previous 32-chunk soft cap.
- intra_attn token-parallel default (BH=4, 1 warp) moves to the gfx950 Triton
  config; other archs keep the previous BH=1, 4-warp default.
- Use get_num_sms() (honors CU_NUM) for routing and segmentation.
- Log the resolved K1, pass A and pass C routes after pass C is settled.
- Pass A and pass C share _solve_u/_update (AMDGCN unchanged).
- Tests: count actual pass-C launches for size routing, cover the segment cap
  and unpublished-schedule fallback, test K1 warp validation via flash_kda_fwd.
vpietila-amd pushed a commit that referenced this pull request Oct 28, 2026
…on gfx950 (#6177)

* [Triton/Gluon] FlashKDA K2: keep a (segment, head)'s V blocks on one XCD

The K2 grid is (cdiv(W, BW), num_segs * H), and every V block of one
(segment, head) re-reads that segment's whole chunk workspace. Workgroups
reach the XCDs round-robin in launch order, x fastest, so with the tuned
BW=16 the eight V blocks of each (segment, head) landed on eight different
XCDs and each pulled the workspace through its own L2.

Measured on MI355X (rocprofv3 FETCH_SIZE/WRITE_SIZE): passes A and C moved
6-8x their compulsory bytes past L2, pinned at ~7 TB/s on every shape --
they were bound by L2-miss traffic, not by the recurrence. Renumbering the
flat program id with the existing remap_xcd, so each XCD owns a contiguous
run of whole (segment, head) groups, brings that traffic to 1.01-1.02x
compulsory and halves those kernels' time at an unchanged grid and tile.

Applied to the Triton segment kernel (passes A and C) and the Gluon pass A,
whose BW=32/64 tiles split V the same way. The remap only changes which
program computes which tile, so results are bit-identical.

FlashKDA end to end, MI355X, bf16, K=V=128:
  gfx950 default route (Gluon K1 + pass A, Triton pass C): 1.24-1.45x
  Triton only (non-gfx950 route):                          1.32-1.61x
  1x512x12:                                                1.01x

* [Triton/Gluon] KDA: load token i once in intra_token_parallel

The token-parallel intra kernel (the default pipeline's path when safe_gate
is off) reloaded token i's q/k/g for every j and every K block. Load them
once outside the j loop, span all of K in one block (BK from K, no longer
tuned) and fold beta into k, following fla. The default config moves from
BH=1 / 4 warps, which idles most of each warp, to BH=4 / 1 warp: within
1.24x of the per-shape best on gfx950.

Default pipeline forward with safe_gate=False on MI355X: 2.0-2.7x
(1x8192x32 2862 -> 1221 us). Aqk/Akk change only in summation order
(output within 2.2e-5 relative).

* [Triton/Gluon] FlashKDA: cap segment length at 64 chunks

_choose_chunks_per_seg capped the segment count at 16 over both of its
reasons, so a long few-head sequence got 256-chunk segments whose serial
walk bounds the recurrence. Cap the occupancy target alone at 16 segments
and then cut segments to at most 64 chunks however many that takes. On
gfx950, 64-chunk segments beat longer ones by up to 1.9x on 1x131072x4.

* [Triton/Gluon] FlashKDA: LDS-prefetching Gluon K2 and a Gluon pass C

Speeds up the Gluon FlashKDA kernels on gfx950 and moves pass C to Gluon:

- K1: sigmoids through exp2 (one v_exp_f32 instead of exp's
  denormal-safe sequence); the gate in a column-per-thread layout, so its
  cumulative sum is in-thread, with tail rows given a zero gate (the last
  and pivot rows are then fixed rows); tail-row masks on the workspace
  dropped; warps split the rows of the [C, K] tiles, so the l2norm sums stay
  in a warp; operands staged through LDS once per product (10 barriers
  instead of 38); the gate load is masked, since buffer loads past the
  tensor's end are not clamped. Launches with waves_per_eu=3 (170 -> 168
  VGPRs, 3 waves/SIMD).
- K1 (Gluon and Triton) writes sigmoid(beta) to a new ws_beta workspace,
  which K2 copies into LDS with the chunk's other tiles.
- Pass A: chunk j+1's tiles are async-copied into a double-buffered LDS
  stage while chunk j computes; one barrier per chunk. The kernel needs
  ~280 VGPRs, so the published schedules carry waves_per_eu=2.
- Pass C: new k2_c_gluon, used for every shape on gfx950. Same prefetch;
  the output projection runs before the state update and is stored from
  the MFMA layout; seg_seq / seg_is_last are read before the loop. It
  supports the paged state_cache read and write-back of the Triton pass C.
  BW=64 unsegmented, BW=128 segmented.

FlashKDA forward on MI355X vs main: 1.43-3.02x (1x8192x12 279 -> 142 us,
1x8192x96 1458 -> 690 us, 1x131072x4 1634 -> 541 us, varlen 1.70-1.99x),
including the two commits before this one. Output within 4.5e-4 relative
of main (K1's l2norm and gate summation order), final state within 3.5e-5.

Tests: test_cases_reach_the_gluon_k2 now checks pass C as well as pass A,
the paged-cache test runs on both K2 routes, and the schedule test covers
the pass C tiles.

* [Triton/Gluon] FlashKDA: skip ws_beta off the Gluon K2 route; name K2 kernels by config

- ws_beta is only read by the Gluon K2, so it is now allocated only when
  K2 takes that route, and both K1s store it only then (STORE_BETA). The
  K2 route is resolved before K1 launches for this.
- The Gluon pass A and pass C names now carry their warp count, through a
  NUM_WARPS constexpr that must equal the launch's num_warps (num_warps
  itself is not a constexpr and would name as NONE), and pass C's also
  carries STATE_V_FIRST.
- test_fast_launch covers the pass C launcher too.

* [Triton/Gluon] FlashKDA: keep small unsegmented pass C on Triton; bench without safe_gate

- On unsegmented shapes with few (sequence, head) pairs, the serial walk
  over the chunks sets pass C's time, and the Triton kernel's lighter
  iteration at BW=16 beats the Gluon one, which cannot go narrower than
  BW=32: 1.08-1.18x for Triton at <= 32 pairs on MI355X, while Gluon wins
  from 40 pairs up (to 2.2x). The gfx950 k2_c_gluon schedule now publishes
  a MIN_BLOCKS_PER_CU floor, as pass A's does, below which pass C runs on
  Triton; ws_beta is then skipped too. Segmented shapes keep the Gluon
  pass C.
- bench_chunk_delta_attn.py gains --no-safe-gate, which takes the
  token-parallel intra kernel.

bench_flash_kda.py default sweep vs main on MI355X: 1.04-2.61x (1x256x12
and 1x1664x12 were 0.91x and 0.94x before this change).

* [Triton/Gluon] FlashKDA: publish the Gluon K1 occupancy hint in the gfx950 config

K1's waves_per_eu=3 was pinned in Python while pass A's occupancy hints are
published in configs/gfx950/gluon/.../chunk_delta_attn/DEFAULT.json. Move it
there (k1_prepare_gluon) and load it through chunk_delta_attn_tuned_config,
so retuning it is a config change. The value holds K1 to ~168 VGPRs, i.e. 3
waves per SIMD; left alone it lands at 170 and drops to 2 (0.85x).

The warp count stays fixed by K1's layouts: the published entry restates it,
and an entry naming another count is rejected rather than launched against
them. Devices without an entry get no hint. Compiled K1 on gfx950 is
unchanged (waves_per_eu=3, 164 VGPRs).

* [Triton/Gluon] KDA: address review — shared Gluon helpers, align wrapper with #6609

- Move sigmoid_log2/fast_sigmoid/sum_combine/l2norm into
  _gluon_kernels/common/utils.py (same definitions as #6609); k1 uses them.
- Align flash_kda.py with #6609: _k2_gluon_schedule(..., kernel) returns a
  Config, use_gluon_k2a/k2c split, _GLUON_K2C_ARCH_OK, unpaged_args; gfx950
  k2 gets gluon_k2_ab_fused/gluon_k2_c launchers mirroring gfx1250's.
- Tests: arch-generic Gluon module imports, pass-C checks gated on arch.
- Fix remap_xcd import path (moved to _triton_kernels/common in #6505).
- Trim verbose comments.

* [Triton/Gluon] KDA: address review — published schedules, resolved-route log, launch-level route tests

- K1 and pass C read their Gluon schedules through _published_gluon_schedule
  and fall back to Triton when none is published (no Python fallback). The
  K1 schedule moves to the wrapper; gluon_k1_prepare rejects a wrong warp count.
- Segment cap moves to the gfx950 Triton config (flash_kda_segments:
  MAX_CHUNKS=64, HARD_CAP). Other archs keep the previous 32-chunk soft cap.
- intra_attn token-parallel default (BH=4, 1 warp) moves to the gfx950 Triton
  config; other archs keep the previous BH=1, 4-warp default.
- Use get_num_sms() (honors CU_NUM) for routing and segmentation.
- Log the resolved K1, pass A and pass C routes after pass C is settled.
- Pass A and pass C share _solve_u/_update (AMDGCN unchanged).
- Tests: count actual pass-C launches for size routing, cover the segment cap
  and unpublished-schedule fallback, test K1 warp validation via flash_kda_fwd.
solinzby1 pushed a commit that referenced this pull request Nov 2, 2026
…on gfx950 (#6177)

* [Triton/Gluon] FlashKDA K2: keep a (segment, head)'s V blocks on one XCD

The K2 grid is (cdiv(W, BW), num_segs * H), and every V block of one
(segment, head) re-reads that segment's whole chunk workspace. Workgroups
reach the XCDs round-robin in launch order, x fastest, so with the tuned
BW=16 the eight V blocks of each (segment, head) landed on eight different
XCDs and each pulled the workspace through its own L2.

Measured on MI355X (rocprofv3 FETCH_SIZE/WRITE_SIZE): passes A and C moved
6-8x their compulsory bytes past L2, pinned at ~7 TB/s on every shape --
they were bound by L2-miss traffic, not by the recurrence. Renumbering the
flat program id with the existing remap_xcd, so each XCD owns a contiguous
run of whole (segment, head) groups, brings that traffic to 1.01-1.02x
compulsory and halves those kernels' time at an unchanged grid and tile.

Applied to the Triton segment kernel (passes A and C) and the Gluon pass A,
whose BW=32/64 tiles split V the same way. The remap only changes which
program computes which tile, so results are bit-identical.

FlashKDA end to end, MI355X, bf16, K=V=128:
  gfx950 default route (Gluon K1 + pass A, Triton pass C): 1.24-1.45x
  Triton only (non-gfx950 route):                          1.32-1.61x
  1x512x12:                                                1.01x

* [Triton/Gluon] KDA: load token i once in intra_token_parallel

The token-parallel intra kernel (the default pipeline's path when safe_gate
is off) reloaded token i's q/k/g for every j and every K block. Load them
once outside the j loop, span all of K in one block (BK from K, no longer
tuned) and fold beta into k, following fla. The default config moves from
BH=1 / 4 warps, which idles most of each warp, to BH=4 / 1 warp: within
1.24x of the per-shape best on gfx950.

Default pipeline forward with safe_gate=False on MI355X: 2.0-2.7x
(1x8192x32 2862 -> 1221 us). Aqk/Akk change only in summation order
(output within 2.2e-5 relative).

* [Triton/Gluon] FlashKDA: cap segment length at 64 chunks

_choose_chunks_per_seg capped the segment count at 16 over both of its
reasons, so a long few-head sequence got 256-chunk segments whose serial
walk bounds the recurrence. Cap the occupancy target alone at 16 segments
and then cut segments to at most 64 chunks however many that takes. On
gfx950, 64-chunk segments beat longer ones by up to 1.9x on 1x131072x4.

* [Triton/Gluon] FlashKDA: LDS-prefetching Gluon K2 and a Gluon pass C

Speeds up the Gluon FlashKDA kernels on gfx950 and moves pass C to Gluon:

- K1: sigmoids through exp2 (one v_exp_f32 instead of exp's
  denormal-safe sequence); the gate in a column-per-thread layout, so its
  cumulative sum is in-thread, with tail rows given a zero gate (the last
  and pivot rows are then fixed rows); tail-row masks on the workspace
  dropped; warps split the rows of the [C, K] tiles, so the l2norm sums stay
  in a warp; operands staged through LDS once per product (10 barriers
  instead of 38); the gate load is masked, since buffer loads past the
  tensor's end are not clamped. Launches with waves_per_eu=3 (170 -> 168
  VGPRs, 3 waves/SIMD).
- K1 (Gluon and Triton) writes sigmoid(beta) to a new ws_beta workspace,
  which K2 copies into LDS with the chunk's other tiles.
- Pass A: chunk j+1's tiles are async-copied into a double-buffered LDS
  stage while chunk j computes; one barrier per chunk. The kernel needs
  ~280 VGPRs, so the published schedules carry waves_per_eu=2.
- Pass C: new k2_c_gluon, used for every shape on gfx950. Same prefetch;
  the output projection runs before the state update and is stored from
  the MFMA layout; seg_seq / seg_is_last are read before the loop. It
  supports the paged state_cache read and write-back of the Triton pass C.
  BW=64 unsegmented, BW=128 segmented.

FlashKDA forward on MI355X vs main: 1.43-3.02x (1x8192x12 279 -> 142 us,
1x8192x96 1458 -> 690 us, 1x131072x4 1634 -> 541 us, varlen 1.70-1.99x),
including the two commits before this one. Output within 4.5e-4 relative
of main (K1's l2norm and gate summation order), final state within 3.5e-5.

Tests: test_cases_reach_the_gluon_k2 now checks pass C as well as pass A,
the paged-cache test runs on both K2 routes, and the schedule test covers
the pass C tiles.

* [Triton/Gluon] FlashKDA: skip ws_beta off the Gluon K2 route; name K2 kernels by config

- ws_beta is only read by the Gluon K2, so it is now allocated only when
  K2 takes that route, and both K1s store it only then (STORE_BETA). The
  K2 route is resolved before K1 launches for this.
- The Gluon pass A and pass C names now carry their warp count, through a
  NUM_WARPS constexpr that must equal the launch's num_warps (num_warps
  itself is not a constexpr and would name as NONE), and pass C's also
  carries STATE_V_FIRST.
- test_fast_launch covers the pass C launcher too.

* [Triton/Gluon] FlashKDA: keep small unsegmented pass C on Triton; bench without safe_gate

- On unsegmented shapes with few (sequence, head) pairs, the serial walk
  over the chunks sets pass C's time, and the Triton kernel's lighter
  iteration at BW=16 beats the Gluon one, which cannot go narrower than
  BW=32: 1.08-1.18x for Triton at <= 32 pairs on MI355X, while Gluon wins
  from 40 pairs up (to 2.2x). The gfx950 k2_c_gluon schedule now publishes
  a MIN_BLOCKS_PER_CU floor, as pass A's does, below which pass C runs on
  Triton; ws_beta is then skipped too. Segmented shapes keep the Gluon
  pass C.
- bench_chunk_delta_attn.py gains --no-safe-gate, which takes the
  token-parallel intra kernel.

bench_flash_kda.py default sweep vs main on MI355X: 1.04-2.61x (1x256x12
and 1x1664x12 were 0.91x and 0.94x before this change).

* [Triton/Gluon] FlashKDA: publish the Gluon K1 occupancy hint in the gfx950 config

K1's waves_per_eu=3 was pinned in Python while pass A's occupancy hints are
published in configs/gfx950/gluon/.../chunk_delta_attn/DEFAULT.json. Move it
there (k1_prepare_gluon) and load it through chunk_delta_attn_tuned_config,
so retuning it is a config change. The value holds K1 to ~168 VGPRs, i.e. 3
waves per SIMD; left alone it lands at 170 and drops to 2 (0.85x).

The warp count stays fixed by K1's layouts: the published entry restates it,
and an entry naming another count is rejected rather than launched against
them. Devices without an entry get no hint. Compiled K1 on gfx950 is
unchanged (waves_per_eu=3, 164 VGPRs).

* [Triton/Gluon] KDA: address review — shared Gluon helpers, align wrapper with #6609

- Move sigmoid_log2/fast_sigmoid/sum_combine/l2norm into
  _gluon_kernels/common/utils.py (same definitions as #6609); k1 uses them.
- Align flash_kda.py with #6609: _k2_gluon_schedule(..., kernel) returns a
  Config, use_gluon_k2a/k2c split, _GLUON_K2C_ARCH_OK, unpaged_args; gfx950
  k2 gets gluon_k2_ab_fused/gluon_k2_c launchers mirroring gfx1250's.
- Tests: arch-generic Gluon module imports, pass-C checks gated on arch.
- Fix remap_xcd import path (moved to _triton_kernels/common in #6505).
- Trim verbose comments.

* [Triton/Gluon] KDA: address review — published schedules, resolved-route log, launch-level route tests

- K1 and pass C read their Gluon schedules through _published_gluon_schedule
  and fall back to Triton when none is published (no Python fallback). The
  K1 schedule moves to the wrapper; gluon_k1_prepare rejects a wrong warp count.
- Segment cap moves to the gfx950 Triton config (flash_kda_segments:
  MAX_CHUNKS=64, HARD_CAP). Other archs keep the previous 32-chunk soft cap.
- intra_attn token-parallel default (BH=4, 1 warp) moves to the gfx950 Triton
  config; other archs keep the previous BH=1, 4-warp default.
- Use get_num_sms() (honors CU_NUM) for routing and segmentation.
- Log the resolved K1, pass A and pass C routes after pass C is settled.
- Pass A and pass C share _solve_u/_update (AMDGCN unchanged).
- Tests: count actual pass-C launches for size routing, cover the segment cap
  and unpublished-schedule fallback, test K1 warp validation via flash_kda_fwd.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants