Repository navigation
[Perf] Add Kimi-K3 gfx950 A8W8 bpreshuffle tuned configs - #6410
Conversation
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
One backend per PR: PR title tags & labels: |
There was a problem hiding this comment.
🟡 Changes recommended
Three new 8-wave rows route supported FP16 calls to a BF16-only kernel.
1 open finding
What changed in this PR
Adds gfx950 Kimi-K3 FP8 bpreshuffle GEMM tuning data.
Changes:
- Adds 87 tuned rows and matching untuned keys.
- Removes nine slower DSv3 rows to prevent collisions.
| File | Description |
|---|---|
dsv3_a8w8_bpreshuffle_tuned_gemm.csv |
Removes superseded rows. |
a8w8_bpreshuffle_untuned_gemm_kimik3.csv |
Adds tuning input shapes. |
a8w8_bpreshuffle_tuned_gemm_kimik3.csv |
Adds tuned gfx950 configurations. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
71872e7 to
76acb56
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The K-divisibility fallback bypasses the CK output-capacity guard, allowing a bf16 output exceeding the documented unsafe 2 GiB threshold to reach CK on gfx950.
1 open issue
1 resolved since last review
Review effort: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
The gfx950 tuning and CK 2-GiB fallback boundary require MI355X correctness and latency validation beyond the available static checks and mocked routing tests.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
The CK fallback passes strided activations without contiguous normalization, producing incorrect results for the reported input case and requiring a fix plus a strided-input regression test.
1 open issue
Review effort: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
The fallback passes strided scale views to CK’s fixed-layout raw-pointer reads, producing incorrectly scaled output; both scales need contiguous storage and regression coverage for strided inputs.
1 open issue
1 resolved since last review
Review effort: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
The gfx950 tuning choices and CK 2 GiB fallback boundary require head-matched GPU correctness and base/head performance validation that was unavailable in this review environment.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
- The FlyDSL launcher raises for M > PRESHUFFLE_M_MAX (65536) and for a K the row's split-K or tile_k does not divide. - getPaddedM(gl=1) serves every M > 8192 with the M=8192 row when N > 4096, so a FlyDSL M=8192 row reaches M > 65536. - A K-padded weight runs at K_w, so a logical-K _ksN row (e.g. _ks7 on K=7168 padded to 8192) fails the K check. - gemm_a8w8_bpreshuffle_flydsl now takes its existing CK fallback in both cases instead of raising. - Fall back only while roundup(M, 256) * N * out_bytes < 2 GiB; past that CK writes wrong rows without error. - Measured on gfx950: CK N=6144 is correct at M=174700 and writes wrong rows at M=174740 (128-row M tile). - Past that bound the FlyDSL launcher error is raised, as on main, e.g. main's 57344x8192 row at M=65537. - Pass contiguous XQ, WQ and scales to CK, as the FlyDSL path does: CK reads them through raw contiguous pointers. - Add meta-tensor tests for both sides of the M limit and the 2 GiB bound, and for ks7/ks2/ks4 K divisibility. - The meta tests mock get_gfx to gfx950, use K-strided XQ and row-strided scales, and assert CK gets contiguous ones.
…K3 rows replace - Remove the gfx950 256-CU DSv3 rows for N=2112 K=7168 at M=1, 2, 4, 8, 16, 32, 64, 96 and 128. - The next commit adds Kimi-K3 rows under the same keys; the family-wide config merge raises on duplicate shapes. - Until then those M use the padded-M lookup over the remaining DSv3 N=2112 K=7168 rows (M >= 256).
- Add 149 gfx950 256-CU rows to a8w8_bpreshuffle_tuned_gemm_kimik3.csv and every new key to the untuned file. - N=7168 K=3584: 55 rows, M=1..256 and M=288..1024 in steps of 32, so M above 256 no longer uses the default kernel. - N=6336 K=7168: 34 rows (M=1..256); N=2112 K=7168: 16 rows (M=1..128), incl. the 9 keys the previous commit freed. - KDA in_proj N=6144 K=7168 (24 rows) and f_a_proj N=128 K=7168 (20 rows), quantized online by ATOM (PTPC FP8). - N=6144 M=544/576/608/640 rows stop padded M 513-640 from using the slower M=1024 CK row (0.95-0.97x). - N=6144 M=8192 is FlyDSL 128x256x128_1x2x4x2: a CK row ran 0.36-0.43x for M > 4096 not a multiple of 256. - N=2112 M=112 is FlyDSL 16x64x512_1x3x4x2: a CK row ran 0.83-0.86x for M 97-111. - 8 N=7168 K=3584 rows were re-tuned on FlyDSL 0.4.0 (M=1/4/8/12/88/96/320/1024), 1.04-1.12x over the old picks. - 13 N=7168 K=3584 CK/cktile rows (M=72..736) and 6336x7168 M=1 use FlyDSL kernels 1.08-1.38x faster. - No FlyDSL 8-wave kernels: they intermittently returned corrupted rows; M=832, 928 and 992 use the M=1024 row. - 6144x7168 is 1.30x geomean (1.20x-1.98x up to M=4096); 128x7168 is 2.22x geomean (1.36x-5.24x) in production A/B. - All FlyDSL rows match the fp32 reference with main's max abs error and are bit-identical across repeated calls.
b0c9fc6 to
9689a39
Compare
- Run the real CK fallback against the fp32 reference for two Kimi-K3 gfx950 256-CU rows the launcher rejects. - Cover M=65537 > PRESHUFFLE_M_MAX on the 6144x7168 M=8192 row and the _ks7 2112x7168 row on a K=8192 padded weight. - Wrap the CK and FlyDSL launchers to assert only CK ran, and assert the tuned FlyDSL row is still selected. - Compare row-strided scales with contiguous ones through both fallback rows with exact checkAllclose (0 tolerance). - Add both shapes to the script's default -s sweep so the summary table covers them. - Run these pytest checks from __main__ too: CI runs this file with python3, which skips its pytest functions
9689a39 to
a424cd8
Compare
- Remove the 11 a8w8 bpreshuffle rows at N=6144, K=7168 (and their untuned lines). With M padding they took over M values from ROCm#6410's rows. - Untuned lists now hold exactly the (M, N, K) of the tuned rows this PR adds. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: fall back to CK for FlyDSL bpreshuffle rows its launcher rejects - The FlyDSL launcher raises for M > PRESHUFFLE_M_MAX (65536) and for a K the row's split-K or tile_k does not divide. - getPaddedM(gl=1) serves every M > 8192 with the M=8192 row when N > 4096, so a FlyDSL M=8192 row reaches M > 65536. - A K-padded weight runs at K_w, so a logical-K _ksN row (e.g. _ks7 on K=7168 padded to 8192) fails the K check. - gemm_a8w8_bpreshuffle_flydsl now takes its existing CK fallback in both cases instead of raising. - Fall back only while roundup(M, 256) * N * out_bytes < 2 GiB; past that CK writes wrong rows without error. - Measured on gfx950: CK N=6144 is correct at M=174700 and writes wrong rows at M=174740 (128-row M tile). - Past that bound the FlyDSL launcher error is raised, as on main, e.g. main's 57344x8192 row at M=65537. - Pass contiguous XQ, WQ and scales to CK, as the FlyDSL path does: CK reads them through raw contiguous pointers. - Add meta-tensor tests for both sides of the M limit and the 2 GiB bound, and for ks7/ks2/ks4 K divisibility. - The meta tests mock get_gfx to gfx950, use K-strided XQ and row-strided scales, and assert CK gets contiguous ones. * chore(configs): drop 9 DSv3 N=2112 K=7168 bpreshuffle rows that Kimi-K3 rows replace - Remove the gfx950 256-CU DSv3 rows for N=2112 K=7168 at M=1, 2, 4, 8, 16, 32, 64, 96 and 128. - The next commit adds Kimi-K3 rows under the same keys; the family-wide config merge raises on duplicate shapes. - Until then those M use the padded-M lookup over the remaining DSv3 N=2112 K=7168 rows (M >= 256). * perf(configs): add Kimi-K3 gfx950 A8W8 bpreshuffle tuned configs - Add 149 gfx950 256-CU rows to a8w8_bpreshuffle_tuned_gemm_kimik3.csv and every new key to the untuned file. - N=7168 K=3584: 55 rows, M=1..256 and M=288..1024 in steps of 32, so M above 256 no longer uses the default kernel. - N=6336 K=7168: 34 rows (M=1..256); N=2112 K=7168: 16 rows (M=1..128), incl. the 9 keys the previous commit freed. - KDA in_proj N=6144 K=7168 (24 rows) and f_a_proj N=128 K=7168 (20 rows), quantized online by ATOM (PTPC FP8). - N=6144 M=544/576/608/640 rows stop padded M 513-640 from using the slower M=1024 CK row (0.95-0.97x). - N=6144 M=8192 is FlyDSL 128x256x128_1x2x4x2: a CK row ran 0.36-0.43x for M > 4096 not a multiple of 256. - N=2112 M=112 is FlyDSL 16x64x512_1x3x4x2: a CK row ran 0.83-0.86x for M 97-111. - 8 N=7168 K=3584 rows were re-tuned on FlyDSL 0.4.0 (M=1/4/8/12/88/96/320/1024), 1.04-1.12x over the old picks. - 13 N=7168 K=3584 CK/cktile rows (M=72..736) and 6336x7168 M=1 use FlyDSL kernels 1.08-1.38x faster. - No FlyDSL 8-wave kernels: they intermittently returned corrupted rows; M=832, 928 and 992 use the M=1024 row. - 6144x7168 is 1.30x geomean (1.20x-1.98x up to M=4096); 128x7168 is 2.22x geomean (1.36x-5.24x) in production A/B. - All FlyDSL rows match the fp32 reference with main's max abs error and are bit-identical across repeated calls. * test: check the FlyDSL-to-CK bpreshuffle fallback numerically on gfx950 - Run the real CK fallback against the fp32 reference for two Kimi-K3 gfx950 256-CU rows the launcher rejects. - Cover M=65537 > PRESHUFFLE_M_MAX on the 6144x7168 M=8192 row and the _ks7 2112x7168 row on a K=8192 padded weight. - Wrap the CK and FlyDSL launchers to assert only CK ran, and assert the tuned FlyDSL row is still selected. - Compare row-strided scales with contiguous ones through both fallback rows with exact checkAllclose (0 tolerance). - Add both shapes to the script's default -s sweep so the summary table covers them. - Run these pytest checks from __main__ too: CI runs this file with python3, which skips its pytest functions



Motivation
Kimi-K3 runs FP8
gemm_a8w8_bpreshuffleshapes on gfx950 that have no tuned row, so they fall back to the default kernel. This PR adds tuned rows for them. It also makesgemm_a8w8_bpreshuffle_flydslfall back to CK for M above the FlyDSL preshuffle limit (65536), which the padded-M lookup would otherwise send to a FlyDSL row.Technical Details
model_configs/a8w8_bpreshuffle_tuned_gemm_kimik3.csv: 149 new rows (gfx950, 256 CU).in_proj(q|k|v|g) andf_a_projGEMMs at TP8.in_projruns in 69 of the 93 layers.model_configs/a8w8_bpreshuffle_untuned_gemm_kimik3.csv: an untuned row for every new tuned key, so re-tuning keeps them.model_configs/dsv3_a8w8_bpreshuffle_tuned_gemm.csv: removes 9 rows for N=2112 K=7168 (M=1..128).usis higher than the deleted row's, but the two files come from separate tuning runs: M=16 is the same kernel in both, listed at 7.48 vs 6.83 us. Timed back to back with the weight read cold, as in a decode step, the new rows are at parity or faster at every M they serve (table below). With the weight hot in cache, M 65–80 and 113–128 are 0.94–0.98x.No rows use the FlyDSL 8-wave preshuffle kernel. For 6144x7168 at M=2048 and 8192, the tuner's 8-wave picks are replaced by the best non-8-wave candidate, which is about 3% and 10% slower than the 8-wave kernel at those M values.
flydsl_bpreshuffle_8w_*) sometimes returns corrupted rows.main; this PR still keeps the non-8-wave picks.FlyDSL 0.4.0 (the version
mainnow pins): every FlyDSL row was re-validated, and 8 N=7168 K=3584 rows (M=1/4/8/12/88/96/320/1024) were re-tuned to faster kernels, 1.04-1.12x over the previous picks.14 more rows were replaced after review: 13 N=7168 K=3584 CK/cktile rows (M=72, 80, 104, 112, 168, 176, 200, 208, 352, 416, 544, 608, 736) and the 6336x7168 M=1 row now use FlyDSL kernels that are 1.08-1.38x faster across every M each row serves, with identical error.
aiter/ops/gemm_op_a8w8.py:getPaddedM(gl=1)serves every M > 8192 with the M=8192 row when N > 4096, and FlyDSL's preshuffle launcher raises for M >PRESHUFFLE_M_MAX(65536). Such calls now fall back to CK only while CK's output still fits: CK bpreshuffle writes wrong rows, with no error, once its bf16 output (M rounded up to the kernel's M tile) reaches 2 GiB (measured on gfx950: N=6144 is correct at M=174700 and wrong at M=174740, which matches a 128-row tile). The guard rounds M up to 256 rows, the largest CK tile, so it is conservative: it stops falling back after M=174592, and M=174593 to 174720 raise the FlyDSL error although CK would still be correct. Past the guard the FlyDSL error is raised, as onmain. FlyDSL rows whose split-K does not divide the runtime K (for example_ks7on a weight padded to K=8192) also fall back to CK instead of raising, under the same 2 GiB output limit. Checked on gfx950 with real kernels against an fp32 dequant reference: M=65537 at N=6144 K=7168, and the_ks7rows at N=2112 with the weight padded to K=8192 (M=1/8/16/48), all run on CK with max abs error <= 1.0 (bf16 rounding) against a reference max of up to 520;test_gemm_a8w8_bpreshuffle_flydsl_ck_fallback_matches_referencecovers both fallbacks numerically and fails if they are removed. A padded K that is not a multiple of 128 (e.g. 7232) is still rejected by CK's heuristic kernels, so such a call raises (inside CK instead of FlyDSL) rather than computing.op_tests/test_gemm_a8w8_bpreshuffle_pad_k.pycovers M=65536 (FlyDSL), M=65537 and M=174592 (CK), and M=174593 plus the 57344x8192 row at M=65537 (FlyDSL error).Overlap with #6959. #6959 dropped its copy of the 20 N=6144 K=7168 rows (6bd43d0), so this PR owns that shape and the two PRs no longer collide.
Test Plan
python3 -m unittest op_tests.tuning_tests.test_config_shape_collisionpytest op_tests/test_gemm_a8w8_bpreshuffle_pad_k.py -k falls_back_to_ck_above_m_maxmainon one MI355X: the same script on both trees, run back to back, alternating, for 3 rounds.run_perftest.mainuses a CK row) with the weight hot in cache, then 17 M values covering every row with the weight rotated throughrun_perftest, so each call reads it cold, as in a decode step.gemm_a8w8_bpreshuffleA/B, 3 alternating rounds, at every tuned M plus M values between rows (59 points). Each new row was then called 30 times against an fp32 reference (1200 calls).Test Result
Setup: MI355X (gfx950), ROCm 7.2, torch 2.11.0+rocm7.2, FlyDSL 0.4.0, compared against merge-base
a6627a454(the padded-M check below uses merge-basee0ba6757d).main(30 tests).main: 290 points (every new row plus a midpoint in each gap between rows, and M=12288 for N=6144 and N=128), productiongemm_a8w8_bpreshufflethroughrun_perftest, 3 alternating rounds, best of each:The few points below 1.0x (0.97x-1.00x, lowest N=6144 M=832) are at parity within run-to-run noise. With the weight read cold, as in a decode step, N=2112 runs 1.14x geomean over 30 M values (1.00x-1.74x); M=8 and M=16 are at parity (M=16 runs the same kernel as
main). The hot-cache direct-kernel sweep in the N=2112 section below is a separate measurement. N=2112 is lower than before becausemainnow has FlyDSL rows for that shape (#6547).main, and are bit-identical across 10 repeated calls. M=65536 runs FlyDSL correctly; M=65537 falls back to CK while CK's output fits under 2 GiB.mainat every point except N=6336 M=184 (1.0005 vs 0.9995, one bf16 step). For the 6144x7168 and 128x7168 rows, the max abs error vs fp32 is identical tomainat every point, and all 1200 repeated calls are correct and bit-identical. Where measured against fp32 (M=264..1024), the max abs error is the same asmain.N=7168 K=3584, M=264..1024 (original FlyDSL 0.3.4.1 run)
N=6144 K=7168 (KDA in_proj): 30 M values, geomean 1.30x (original FlyDSL 0.3.4.1 run)
flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x2x0x2_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x2x0x1_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x1x0x1_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x2x0x1_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x0x0x1_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x0x0x1_flydsl_bpreshuflle_32x32x1024_F8_F8_B16_0x1x0x2_flydsl_bpreshuflle_32x64x512_F8_F8_B16_1x2x0x2_dflydsl_bpreshuflle_48x128x256_F8_F8_B16_1x3x4x2_flydsl_bpreshuflle_48x128x256_F8_F8_B16_1x3x4x2_flydsl_bpreshuflle_32x64x512_F8_F8_B16_1x4x4x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x0x4x2_dflydsl_bpreshuflle_80x64x256_F8_F8_B16_1x2x0x2_dflydsl_bpreshuflle_80x64x256_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_96x64x256_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_64x128x256_F8_F8_B16_1x0x4x2_flydsl_bpreshuflle_64x128x256_F8_F8_B16_1x0x4x2_flydsl_bpreshuflle_64x192x256_F8_F8_B16_0x0x4x2_flydsl_bpreshuflle_64x192x256_F8_F8_B16_0x0x4x2_flydsl_bpreshuflle_64x192x256_F8_F8_B16_1x2x4x2_flydsl_bpreshuflle_64x192x256_F8_F8_B16_1x2x4x2_a8w8_bpreshuffle_256x128x192x256_16x16_16x16_16xa8w8_bpreshuffle_256x128x192x256_16x16_16x16_16xflydsl_bpreshuflle_128x192x256_F8_F8_B16_1x1x4x1flydsl_bpreshuflle_128x192x256_F8_F8_B16_1x1x4x1flydsl_bpreshuflle_128x256x128_F8_F8_B16_1x2x4x2flydsl_bpreshuflle_128x256x128_F8_F8_B16_1x2x4x2a8w8_bpreshuffle_256x256x256x128_16x16_16x16_8x3a8w8_bpreshuffle_256x256x256x128_16x16_16x16_8x3a8w8_bpreshuffle_256x256x256x128_16x16_16x16_8x3N=128 K=7168 (KDA f_a_proj): 29 M values, geomean 2.22x (original FlyDSL 0.3.4.1 run)
flydsl_bpreshuflle_16x32x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_0x3x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x4x0x2_dflydsl_bpreshuflle_16x32x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_32x32x512_F8_F8_B16_0x4x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_0x3x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_0x0x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x0x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x0x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x2x0x2_dflydsl_bpreshuflle_16x64x512_F8_F8_B16_1x2x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_0x2x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_0x2x0x2_dflydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x0x0x2_flydsl_bpreshuflle_16x32x1024_F8_F8_B16_0x0x0x2_flydsl_bpreshuflle_32x64x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x3x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_32x64x512_F8_F8_B16_1x1x0x2_dflydsl_bpreshuflle_64x128x256_F8_F8_B16_1x0x0x2_flydsl_bpreshuflle_64x128x256_F8_F8_B16_1x0x0x2_N=2112 K=7168 vs the deleted DSv3 rows (FlyDSL 0.4.0, cold weights)
Each M uses the row the padded-M lookup picks on that side, so M=65–80 and 97–112 move from
main's M=128 row to this PR's new M=80 and M=112 rows.All outputs match the fp32 reference. In the hot-cache sweep, M=1–4 run 1.44–1.53x and every other M is at parity or faster except M 65–80 and 113–128 (0.94–0.98x); a decode step reads the weight cold, as above.
Padded-M check. For M without an exact row, the a8w8 bpreshuffle lookup uses M rounded up (16/32/64/128 by size), then the next power of two (8192 once M > 8192 and N > 4096), so each row also serves the uneven M below it. Base-vs-head A/B (base = merge-base
e0ba6757d;aiter.gemm_a8w8_bpreshufflewithrun_perftest, 3 interleaved rounds, one MI355X) on 2112x7168 (107 points) and 6144x7168 (143 points), covering every row, the row minus 1, the previous row plus 1, the midpoint, and odd M up to 49153. Errors match base at every point.128x256x128_1x2x4x2and16x64x512_1x3x4x2.80x192x256_1x1x4x2) were added because the CK M=1024 row ran 0.95–0.97x for M 513–640.256x256x128kernels were faster but gave intermittently wrong results at M ≥ 8193, so they are not used.Submission Checklist
🤖 Generated with Claude Code