Skip to content

[Config] Add Kimi-K3 MI355X GEMM tuning tables - #6959

Open
aryatsc wants to merge 10 commits into
ROCm:mainfrom
modal-projects:devin/k3-gemm-tables-1793030216
Open

aryatsc wants to merge 10 commits into
ROCm:mainfrom
modal-projects:devin/k3-gemm-tables-1793030216

Conversation

@aryatsc

@aryatsc aryatsc commented Oct 26, 2026 •

Copy link
Copy Markdown

Motivation

Kimi-K3's decode and dense-FP8 linears on gfx950 (TP8) fall back to untuned a8w8/bf16 GEMM configs.

Modifications

  • Kimi-K3 decode/dense rows folded into the existing kimik3_bf16_* and a8w8_bpreshuffle_*_kimik3 tuned/untuned files. No new files.
  • No rows at a8w8 N=6144, K=7168: [Perf] Add Kimi-K3 gfx950 A8W8 bpreshuffle tuned configs #6410 owns that shape.
  • The 10 bf16 decode rows that picked hipblaslt (M=8-64 N=7168 K=43008, M=40/56 N=20480 K=7168) use torch/flydsl winners instead (retune by @yuychang, co-authored). They are 0.9-5.7% slower than the same-session hipblaslt time.
  • The untuned lists hold exactly the (M, N, K) of the tuned rows this PR adds. The bf16 untuned list also keeps 11 prefill shapes (lines 418-428) that only have gfx1250 rows, so a gfx950 re-tune from it would tune those too.

Tests

test_config_shape_collision reports no duplicate shapes for these files. Used by the Kimi-K3 static FP8 dense PR in SGLang.

PR Self-Review

Reviewed revision

2147087

Behavior and intent

Only tuned-config CSVs change: 42 a8w8 bpreshuffle rows and 24 bf16 rows (8 of the bf16 rows replace existing kimik3 rows at the same key). No code changes.

Validation and unverified scope

  • python3 -m unittest op_tests.tuning_tests.test_config_shape_collision in the MI355X serving image: no duplicate-shape failures in the bf16 or a8w8 bpreshuffle families. (Two other tests in that file need newer aiter/jit code than the image and fail the same way on clean main.)
  • The 10 retuned bf16 rows went through gemm_a16w16_tune.py --run_config (status OK, @yuychang).
  • The other 64 rows (the diff adds 32 bf16 rows: 24 new, 8 replacing main's) were tuned on MI355X (gfx950, cu_num 256) with the AITER tuners (gemm_a8w8_bpreshuffle_tune.py, gemm_a16w16_tune.py) on our Kimi-K3 serving image. The exact tuner commit and flags for these rows were not recorded; every a8w8 row has splitK=0. They have no --run_config replay on the current main.
  • The per-M comparison below uses each row's recorded tuning time. A row tuned at one M and served at a padded M is not a same-M A/B.
  • Unverified: end-to-end gain from this exact table set on upstream SGLang; our serving deploy uses an equivalent table from our fork.

PR-specific question

Lookups are gfx950 / cu_num 256 only, keyed on bf16 (M, N, K, bias, dtype, outdtype, scaleAB, bpreshuffle) and a8w8 bpreshuffle (M, N, K, q_dtype_w). A row also serves nearby M values: tuned_gemm.py and gemm_op_a8w8.py try exact M, then getPaddedM level 0 (next multiple of 16/32/64/128), then level 1 (next power of two, capped at 8192 when N > 4096). So a new row changes the selection for every M that pads to it before reaching an existing row.

The table below is the full set of selection changes. It replays that lookup for M = 1..32768 over the merged family (configs/<family>.csv plus every model_configs/*<family>*.csv) on main and on this head. Shapes not listed keep their selection.

  • 2 a8w8 shapes and 2 bf16 shapes had no row on main (N=768/6016 K=7168 a8w8; N=144 K=7168 and N=7168 K=43008 bf16), so the change there is default → tuned.
  • Elsewhere a new row takes M values from an existing row. The rows that lose M values are: a8w8 N=3584 K=7168 M=64 (for M=56), N=7168 K=512 M=64 in the glm5.2 file (for M=56), and N=7168 K=1792 M=48 (for M=40). For bf16 they are N=896 K=3584 M=8/16/32/48/64, N=1536 K=128 M=16/32/48/64, N=7168 K=1536 M=48, and N=20480 K=7168 M=48/64.
  • bf16 N=1536 K=128: only the M=40 Triton row changes the kernel main would pick (recorded 3.23 µs vs FlyDSL M=48 row 2.76 µs, not an A/B). triton_gemm ignores the row's config, so M=56 makes the same Triton call either way, and M=17-32 follow the M=24 Triton row. Every other change has a lower recorded time.

Changed op tests

None

Per-M selection, main vs this head

a8w8_bpreshuffle N=768 K=7168

M main this PR
5-8 no row (default) M=8 flydsl 6.9805µs
9-16 no row (default) M=16 flydsl 7.3195µs
17-23 no row (default) M=32 flydsl 7.1356µs
24 no row (default) M=24 flydsl 6.9219µs
25-32 no row (default) M=32 flydsl 7.1356µs
33-39 no row (default) M=48 flydsl 7.1809µs
40 no row (default) M=40 flydsl 7.2564µs
41-48 no row (default) M=48 flydsl 7.1809µs
49-55 no row (default) M=64 flydsl 7.1556µs
56 no row (default) M=56 flydsl 7.0025µs
57-64 no row (default) M=64 flydsl 7.1556µs

a8w8_bpreshuffle N=3584 K=7168

M main this PR
56 M=64 flydsl 10.7727µs M=56 flydsl 9.802µs

a8w8_bpreshuffle N=6016 K=7168

M main this PR
1 no row (default) M=1 flydsl 8.9508µs
2 no row (default) M=2 flydsl 9.0277µs
3-4 no row (default) M=4 flydsl 9.154µs
5-8 no row (default) M=8 flydsl 9.1235µs
9-16 no row (default) M=16 flydsl 9.7707µs
17-23 no row (default) M=32 flydsl 11.7482µs
24 no row (default) M=24 flydsl 10.9996µs
25-32 no row (default) M=32 flydsl 11.7482µs
33-39 no row (default) M=48 cktile 15.3141µs
40 no row (default) M=40 cktile 14.9915µs
41-48 no row (default) M=48 cktile 15.3141µs
49-55 no row (default) M=64 flydsl 14.5979µs
56 no row (default) M=56 flydsl 14.3068µs
57-64 no row (default) M=64 flydsl 14.5979µs
65-71 no row (default) M=80 flydsl 19.6848µs
72 no row (default) M=72 flydsl 18.9864µs
73-80 no row (default) M=80 flydsl 19.6848µs
81-87 no row (default) M=96 flydsl 20.1734µs
88 no row (default) M=88 flydsl 19.8312µs
89-96 no row (default) M=96 flydsl 20.1734µs
97-103 no row (default) M=112 cktile 20.5839µs
104 no row (default) M=104 cktile 20.4731µs
105-112 no row (default) M=112 cktile 20.5839µs
113-119 no row (default) M=128 ck 20.7988µs
120 no row (default) M=120 cktile 20.6189µs
121-128 no row (default) M=128 ck 20.7988µs
129-176 no row (default) M=256 flydsl 25.3278µs
177-192 no row (default) M=192 flydsl 23.1267µs
193-256 no row (default) M=256 flydsl 25.3278µs
257-352 no row (default) M=512 flydsl 33.6756µs
353-384 no row (default) M=384 flydsl 30.0557µs
385-512 no row (default) M=512 flydsl 33.6756µs
513-736 no row (default) M=1024 flydsl 50.8153µs
737-768 no row (default) M=768 flydsl 41.7228µs
769-1024 no row (default) M=1024 flydsl 50.8153µs
1025-1472 no row (default) M=2048 flydsl 79.6261µs
1473-1536 no row (default) M=1536 flydsl 67.3911µs
1537-2048 no row (default) M=2048 flydsl 79.6261µs
2049-3008 no row (default) M=4096 flydsl 151.1194µs
3009-3072 no row (default) M=3072 flydsl 122.9702µs
3073-4096 no row (default) M=4096 flydsl 151.1194µs
4097-16256 no row (default) M=8192 flydsl 244.624µs
16257-16384 no row (default) M=16384 flydsl 481.1443µs
16385-32768 no row (default) M=8192 flydsl 244.624µs

a8w8_bpreshuffle N=7168 K=512

M main this PR
56 M=64 flydsl 3.8674µs [a8w8_bpreshuffle_tuned_gemm_glm5.2.csv] M=56 flydsl 3.2712µs

a8w8_bpreshuffle N=7168 K=1792

M main this PR
40 M=48 ck 7.7143µs M=40 flydsl 6.7691µs

bf16 N=144 K=7168

M main this PR
9-16 no row (default) M=16 flydsl 4.7084µs
17-23 no row (default) M=32 flydsl 5.0922µs
24 no row (default) M=24 flydsl 5.0605µs
25-32 no row (default) M=32 flydsl 5.0922µs
33-39 no row (default) M=48 flydsl 6.1238µs
40 no row (default) M=40 flydsl 5.8794µs
41-48 no row (default) M=48 flydsl 6.1238µs
49-55 no row (default) M=64 flydsl 5.5693µs
56 no row (default) M=56 flydsl 6.0374µs
57-64 no row (default) M=64 flydsl 5.5693µs

bf16 N=896 K=3584

M main this PR
5-8 M=8 flydsl 5.4581µs M=8 flydsl 5.1793µs
9-16 M=16 flydsl 5.6084µs M=16 flydsl 5.3296µs
17-23 M=32 flydsl 6.0403µs M=32 flydsl 5.6272µs
24 M=32 flydsl 6.0403µs M=24 flydsl 5.426µs
25-32 M=32 flydsl 6.0403µs M=32 flydsl 5.6272µs
33-39 M=48 flydsl 6.6418µs M=48 flydsl 6.2146µs
40 M=48 flydsl 6.6418µs M=40 flydsl 6.2315µs
41-48 M=48 flydsl 6.6418µs M=48 flydsl 6.2146µs
49-55 M=64 flydsl 7.0452µs M=64 flydsl 6.6791µs
56 M=64 flydsl 7.0452µs M=56 flydsl 6.6074µs
57-64 M=64 flydsl 7.0452µs M=64 flydsl 6.6791µs

bf16 N=1536 K=128

M main this PR
9-16 M=16 triton 2.5438µs M=16 triton 2.24µs
17-23 M=32 flydsl 2.7295µs M=32 triton 2.36µs
24 M=32 flydsl 2.7295µs M=24 triton 2.82µs
25-32 M=32 flydsl 2.7295µs M=32 triton 2.36µs
40 M=48 flydsl 2.7599µs M=40 triton 3.23µs
56 M=64 triton 2.6261µs M=56 triton 3.46µs

bf16 N=7168 K=1536

M main this PR
33-39 M=48 flydsl 8.1092µs M=48 flydsl 7.9519µs
40 M=48 flydsl 8.1092µs M=40 flydsl 7.4874µs
41-48 M=48 flydsl 8.1092µs M=48 flydsl 7.9519µs

bf16 N=7168 K=43008

M main this PR
5-8 no row (default) M=8 torch 97.1685µs
9-16 no row (default) M=16 torch 100.2453µs
17-23 no row (default) M=32 flydsl 102.8149µs
24 no row (default) M=24 flydsl 101.494µs
25-32 no row (default) M=32 flydsl 102.8149µs
33-39 no row (default) M=48 flydsl 106.3688µs
40 no row (default) M=40 flydsl 107.3934µs
41-48 no row (default) M=48 flydsl 106.3688µs
49-55 no row (default) M=64 flydsl 109.0135µs
56 no row (default) M=56 flydsl 108.214µs
57-64 no row (default) M=64 flydsl 109.0135µs

bf16 N=20480 K=7168

M main this PR
40 M=48 flydsl 52.1294µs M=40 flydsl 51.4526µs
56 M=64 flydsl 63.0612µs M=56 flydsl 54.4859µs

Tuned a8w8 bpreshuffle tables for the Kimi-K3 decode and dense FP8 linears
and a tuned bf16 table for its decode GEMMs (TP8, gfx950), plus the untuned
shape lists they were tuned from (dense FP8, prefill bf16).

The new decode bf16 table retunes the M=8/16/32/48/64, N=896, K=3584
shapes, so their rows are removed from kimik3_bf16_tuned_gemm.csv to keep
one row per shape in the bf16_tuned_gemm merge.

Co-Authored-By: Devin AI <devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 3be28ce)
@aryatsc
aryatsc requested a review from a team October 26, 2026 19:07
@github-actions github-actions Bot changed the title Add Kimi-K3 MI355X GEMM tuning tables [Config] Add Kimi-K3 MI355X GEMM tuning tables Oct 26, 2026
@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 6959 --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.

@yuychang

Copy link
Copy Markdown
Contributor

Hi @aryatsc, heads-up: #6410 adds the same 20 gfx950 keys for N=6144, K=7168 (M=1, 2, 4, ..., 8192) in a8w8_bpreshuffle_tuned_gemm_kimik3.csv that this PR adds in a8w8_bpreshuffle_tuned_gemm_kimik3dense.csv. With both merged, the config load fails with "duplicate shape entries" for a8w8_bpreshuffle_tuned_gemm.

Whichever PR merges second should drop its copy of those 20 rows. One note: this PR's M=8192 row uses the FlyDSL 8-wave kernel, and because of padded-M lookup it also serves every M above 8192. #6410's rows avoid the 8-wave kernel and measure 1.41x vs main on this shape. Happy to coordinate either way.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@aryatsc

aryatsc commented Oct 28, 2026

Copy link
Copy Markdown
Author

Hi @aryatsc, heads-up: #6410 adds the same 20 gfx950 keys for N=6144, K=7168 (M=1, 2, 4, ..., 8192) in a8w8_bpreshuffle_tuned_gemm_kimik3.csv that this PR adds in a8w8_bpreshuffle_tuned_gemm_kimik3dense.csv. With both merged, the config load fails with "duplicate shape entries" for a8w8_bpreshuffle_tuned_gemm.

Whichever PR merges second should drop its copy of those 20 rows. One note: this PR's M=8192 row uses the FlyDSL 8-wave kernel, and because of padded-M lookup it also serves every M above 8192. #6410's rows avoid the 8-wave kernel and measure 1.41x vs main on this shape. Happy to coordinate either way.

Thanks for catching this. We dropped our copy of the 20 N=6144, K=7168 rows (6bd43d0), so #6410 owns that shape. test_config_shape_collision passes on this branch. With #6410's kimik3 file added, it reports 9 duplicates between that file and dsv3_a8w8_bpreshuffle_tuned_gemm.csv, none from this PR.

gfx,cu_num,M,N,K,bias,dtype,outdtype,scaleAB,bpreshuffle,libtype,solidx,splitK,us,kernelName,err_ratio,tflops,bw
gfx950,256,16,144,7168,False,torch.bfloat16,torch.bfloat16,False,False,flydsl,0,8,4.7084,flydsl_hgemm_abf16_wbf16_bf16_t16x16x128x8_ks8_w1x1x1_bias0_ktail0_gm0_pft_gfx950,0.009,7.02,488.14
gfx950,256,24,144,7168,False,torch.bfloat16,torch.bfloat16,False,False,flydsl,0,8,5.0605,flydsl_hgemm_abf16_wbf16_bf16_t32x16x128x5_ks8_w2x1x1_bias0_ktail0_gm0_pft_gfx950,0.0082,9.79,477.29
gfx950,256,32,144,7168,False,torch.bfloat16,torch.bfloat16,False,False,flydsl,0,8,5.0922,flydsl_hgemm_abf16_wbf16_bf16_t32x16x128x4_ks8_w2x1x1_bias0_ktail0_gm0_pft_gfx950,0.0076,12.97,497.3

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.

better keep only 1 tuned_gemm.csv . Agent can help extract certain shapes from it. Same as a8w8.

…ig files

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@aiter-bot

aiter-bot commented Oct 29, 2026 •

Copy link
Copy Markdown

WAITING ON AUTHOR

PR Self-Review

Revision 77eb4f9b69ed · mode shadow

Waiting for the PR author. The readiness report will appear after the self-review answers are complete for this revision.

Questions to answer in the PR description

  1. What behavior does this PR change, and why is the existing behavior insufficient?
  2. What did you validate, and what remains unverified? Include commands, hardware/configuration and results.
  3. Which exact workload keys consume these configuration changes, and what evidence shows that unrelated shapes keep their previous selection?
  4. For every changed op_tests/ file, what behavior does it cover and why is it not redundant? Write None if no op test changed.

Edit the PR Self-Review section in the PR description. Include the current revision SHA; this comment refreshes automatically.

Missing or invalid answers

  • FAIL — Reviewed revision must contain at least the first 7 characters of the current head SHA

Shadow mode reports what would block but keeps the status successful. Set AITER_PR_SELF_REVIEW_MODE=enforce only after reviewing real false positives.

@aryatsc

aryatsc commented Oct 29, 2026

Copy link
Copy Markdown
Author

Following up on the review comment about keeping a single tuned CSV per family: since this branch doesn't allow maintainer edits, I pushed two commits on top of this PR to yuychang:k3/kimik3-gemm-tables-single-csv. Feel free to cherry-pick them:

git fetch https://github.com/yuychang/aiter.git k3/kimik3-gemm-tables-single-csv
git cherry-pick 14ff2ccc3..FETCH_HEAD

1. refactor: fold the new tables into the existing kimik3 files

New file in this PR Folded into
kimik3_decode_bf16_tuned_gemm.csv kimik3_bf16_tuned_gemm.csv
a8w8_bpreshuffle_tuned_gemm_kimik3decode.csv, ..._kimik3dense.csv a8w8_bpreshuffle_tuned_gemm_kimik3.csv
kimik3_prefill_bf16_untuned_gemm.csv kimik3_bf16_untuned_gemm.csv (30 of its 45 shapes were already there)
kimik3_dense_fp8_untuned_gemm.csv a8w8_bpreshuffle_untuned_gemm_kimik3.csv
The diff against the merge-base now touches only those 4 existing files and adds no new files. python3 -m unittest op_tests.tuning_tests.test_config_shape_collision passes.

2. fix: drop the hipblaslt rows

The decode bf16 table had 10 hipblaslt rows. hipblaslt solutions aren't persistent across Docker images, so I retuned those shapes with csrc/gemm_a16w16/gemm_a16w16_tune.py (asm/flydsl/triton/torch/skinny, gfx950, splitK on). I also re-measured hipblaslt in the same session for a like-for-like comparison:

M N K hipblaslt (us) new (us) new libtype Δ
8 7168 43008 94.72 97.17 torch +2.6%
16 7168 43008 94.87 100.25 torch +5.7%
24 7168 43008 97.91 101.49 flydsl +3.7%
32 7168 43008 100.51 102.81 flydsl +2.3%
40 7168 43008 102.40 107.39 flydsl +4.9%
48 7168 43008 104.09 106.37 flydsl +2.2%
56 7168 43008 106.33 108.21 flydsl +1.8%
64 7168 43008 108.09 109.01 flydsl +0.9%
40 20480 7168 50.05 51.45 flydsl +2.8%
56 20480 7168 51.69 54.49 flydsl +5.4%
The non-hipblaslt kernels are 0.9–5.7% slower on these shapes. They're memory-bound (the K=43008 weight is ~616 MB), so there's little headroom. All 10 rows pass the production operator via --run_config (status OK).

Side note, separate from this PR: on M=40/48/64 (K=43008) and M=40 (N=20480), opus candidates crashed tuner workers (HSA_STATUS_ERROR_OUT_OF_RESOURCES / GPU OOM), so those 4 shapes were tuned without opus. Where opus was fully measured (M=40, K=43008), its best was ~139 us, so no winner is lost.

Done in 5b96cdb. The decode/dense rows now live in the existing kimik3 files, one tuned and one untuned file per kind:

The separate kimik3decode/kimik3dense/decode/prefill/dense files are gone. test_config_shape_collision reports no duplicate shapes for these files.

aryatsc and others added 2 commits October 29, 2026 20:47
- Replace the 10 hipblaslt rows (M=8..64 N=7168 K=43008, M=40/56 N=20480 K=7168) with torch/flydsl winners
- Retuned with gemm_a16w16_tune.py on gfx950; 0.9-5.7% slower than same-session hipblaslt
- Verified all 10 rows through --run_config (status OK); test_config_shape_collision passes

Co-authored-by: Yu-Yun Chang <yuyun.chang@amd.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@waqahmed-amd-fi waqahmed-amd-fi 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.

Static review of bafbd99: nothing was run, and every timing below is a us value read from the CSVs. GitHub CI ran only the title-tag and self-review workflows on this head; the test workflows wait for maintainer approval because the branch is on a fork. The description quoted is the version edited on Oct 29 at 21:11 UTC.

Tasks 1 and 2 should be resolved before merge. Task 3 is at your discretion, and the design question does not block.

Tasks

  1. Drop the 11 N=6144, K=7168 rows, or A/B them against the rows from #6410
  2. Correct the description's claims about which selections change
  3. A/B the switch to Triton for M=17–32 and M=40 at N=1536, K=128

Lower importance

  • Nit: Keep the untuned lists in step with the tuned rows

Design questions

  • Were the a8w8 rows tuned without --splitK? Neither approach is presumed wrong.

Heads-up

  • Seven hipblaslt rows (N=6016, K=7168, lines 52, 54, 56 and 65–68 of kimik3_bf16_tuned_gemm.csv) predate this PR and have the persistence problem bafbd99 fixed for its own rows, so they could be a follow-up.

gfx950,256,24,6016,7168,torch.float8_e4m3fn,flydsl,2375,0,10.9996,flydsl_bpreshuflle_32x32x1024_F8_F8_B16_0x0x0x1_default,188.18,3962.28,0.0
gfx950,256,32,6016,7168,torch.float8_e4m3fn,flydsl,2315,0,11.7482,flydsl_bpreshuflle_32x32x1024_F8_F8_B16_0x0x0x2_default,234.92,3722.88,0.0
gfx950,256,40,6016,7168,torch.float8_e4m3fn,cktile,37,0,14.9915,a8w8_bpreshuffle_cktile_0x0x8x4x1x0x0x0x0x2_32x64x512_1x4x1_16x16x128_default,230.12,2927.7,0.0
gfx950,256,48,6016,7168,torch.float8_e4m3fn,cktile,37,0,15.3141,a8w8_bpreshuffle_cktile_0x0x8x4x1x0x0x0x0x2_32x64x512_1x4x1_16x16x128_default,270.32,2876.06,0.0

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.

Design question: Were the a8w8 rows tuned without --splitK?

All 53 new a8w8 rows have splitK 0 and no _ksN kernel, and the tuner adds FlyDSL split-K candidates only under --splitK, which is off by default (gemm_a8w8_bpreshuffle_tune.py:543, base_tuner.py:141). At the 20 N=6144, K=7168 keys this PR carried until 6bd43d0, its picks matched #6410's within 2.3% where both chose the same kernel, but were 1–4% slower at M=32, 128 and 256 and 7–23% slower at M=48, 72, 80 and 96, where #6410 picked split-K kernels. At M=48 the slower pick was the same 32x64x512 CKTile tile as this row, at 15.09 µs against 13.66 µs.

If --splitK was off, a re-tune with it may find faster kernels for these N=6016 rows and the N=768 ones. Those shapes had no tuned rows before, so this need not block, and leaving split-K out on purpose is also fine.

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.

Are deterministic solutions all you need?

gfx950,256,4096,6016,7168,torch.float8_e4m3fn,flydsl,1000033,0,151.1194,flydsl_bpreshuffle_8w_256x256x128_F8_F8_B16_4x0,2337.63,805.76,0.0
gfx950,256,8192,6016,7168,torch.float8_e4m3fn,flydsl,1000028,0,244.624,flydsl_bpreshuffle_8w_256x256x128_F8_F8_B16_2x4,2888.2,819.25,0.0
gfx950,256,16384,6016,7168,torch.float8_e4m3fn,flydsl,1000031,0,481.1443,flydsl_bpreshuffle_8w_256x256x128_F8_F8_B16_3x4,2936.84,743.43,0.0
gfx950,256,40,6144,7168,torch.float8_e4m3fn,cktile,151,0,14.9501,a8w8_bpreshuffle_cktile_0x0x8x4x1x0x0x0x0x3_32x64x512_1x4x1_16x16x128_default,235.67,2997.87,0.0

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.

Task 1: Drop the 11 N=6144, K=7168 rows, or A/B them against the rows from #6410 (category: performance)

Lines 1207–1217 add 11 rows for the shape the Oct 28 reply said #6410 owns. Because the lookup also tries padded M (gemm_op_a8w8.py:581), they take over M values that #6410's rows serve today: M=40 from its M=48 row (line 1350), 88 from M=96 (line 1355), 97–112 and 120 from M=128 (line 1356), and 177–192, 353–384, 737–768, 1473–1536, 3009–3072 and 16257–16384. At M=40, 88, 97–112 and 120, #6410's kernels need no more M tiles than at their own M and record 13.66, 16.13 and 18.70 µs; the new rows record 14.95, 16.80 and 20.52–20.55 µs. The two tuning runs agree within 2.3% wherever they chose the same N=6144 kernel (this PR's rows before 6bd43d0), and at M=48 this PR's earlier pick, the CKTile kernel this row uses, recorded 15.09 µs against #6410's 13.66 µs. So those M probably run 4–10% slower, though nothing here was measured. The collision guard passes because it compares exact keys.

Could these 11 rows be dropped? Keeping a row is fine if a back-to-back A/B of gemm_a8w8_bpreshuffle against main, alternating rounds as #6410 did, shows it faster at every M it takes over.

gfx950,256,16384,7168,1792,False,torch.bfloat16,torch.bfloat16,False,False,torch,0,0,310.0445,native,0.0,1357.57,1029.82
gfx950,256,32768,7168,1024,False,torch.bfloat16,torch.bfloat16,False,False,torch,0,0,382.655,native,0.0,1257.1,1441.38
gfx950,256,32768,7168,1792,False,torch.bfloat16,torch.bfloat16,False,False,torch,0,0,622.2538,native,0.0,1352.85,984.96
gfx950,256,8,896,3584,False,torch.bfloat16,torch.bfloat16,False,False,flydsl,403,7,5.4581,flydsl_hgemm_abf16_wbf16_bf16_t16x32x64x8_ks7_w1x1x1_bias0_ktail0_gm0_pft_gfx950,0.0197,9.41,1189.83

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.

Task 2: Correct the description's claims about which selections change (category: docs)

Answering the self-review question for config PRs, the description says these rows are consumed only by lookups "whose full key matches a row" and that "every other shape keeps its previous selection", citing the collision guard. Both lookups also try padded M (tuned_gemm.py:134, gemm_op_a8w8.py:581), so a row also serves the M values that pad to it, and the guard compares exact keys only. At this head 23 new rows take over M values from main's rows (Task 1 has the N=6144 ones), and the diff replaces 8 existing bf16 rows, starting with this deleted one; the description mentions neither. It also names no tuning stack or tuner flags for the 75 rows from 04db8eb; only the 10 retuned rows have a --run_config result.

Could you rewrite that answer from a per-M comparison of the selected row on main and on this head, list the replaced rows, and name the stack and tuner flags behind the 75 older rows?

Comment on lines +758 to +760
gfx950,256,24,1536,128,False,torch.bfloat16,torch.bfloat16,False,False,triton,0,0,2.82,auto,0.0,3.35,167.76
gfx950,256,32,1536,128,False,torch.bfloat16,torch.bfloat16,False,False,triton,0,0,2.36,auto,0.0,5.33,211.74
gfx950,256,40,1536,128,False,torch.bfloat16,torch.bfloat16,False,False,triton,0,0,3.23,auto,0.0,4.87,162.95

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.

Task 3: A/B the switch to Triton for M=17–32 and M=40 at N=1536, K=128 (category: performance, at your discretion)

These rows move M=17–32 and M=40 at N=1536, K=128 from main's FlyDSL kernel (t16x16x64x3_ks1, whose M=32 and M=48 rows record 2.73 and 2.76 µs) to Triton, which records 2.82 µs at M=24, 2.36 at M=32 and 3.23 at M=40. That kernel needs no more 16-row tiles at M=24 and 40 than at 32 and 48, and in this tuning run the same FlyDSL kernels read 5–8% faster than main's rows did (line 749 against the deleted line 630, and line 762 at M=40 against the deleted M=48 line 697). So M=24 and M=40 probably get slower and M=32 may get faster; Triton's times here fall from M=24 to M=32, so they look noisy, and nothing here was measured.

Could you time Triton and main's FlyDSL kernel back to back at M=24, 32 and 40, as #6410 did for the DSv3 rows it replaced, and keep main's rows where Triton does not win? The same check would settle the other five kernel switches (lines 750, 752, 754, 756 and 763), whose 1.9–6.8% CSV gains are within that drift.

1024,12,7168,False,torch.bfloat16,torch.bfloat16,False,False
2048,12,7168,False,torch.bfloat16,torch.bfloat16,False,False
4096,12,7168,False,torch.bfloat16,torch.bfloat16,False,False
16384,1536,7168,False,torch.bfloat16,torch.bfloat16,False,False

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.

Nit: Keep the untuned lists in step with the tuned rows. 14 of the 15 shapes added from here have no tuned row, and 35 of the new tuned rows (24 bf16, 11 a8w8) have no untuned row, so a --all re-tune would skip those 35 and tune the 14; #6410 added an untuned row for every tuned key.

- 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>
@aryatsc

aryatsc commented Oct 30, 2026

Copy link
Copy Markdown
Author

Thanks, this was a thorough review. Changes are in 2147087:

  1. N=6144, K=7168: dropped all 11 a8w8 rows and their untuned lines. [Perf] Add Kimi-K3 gfx950 A8W8 bpreshuffle tuned configs #6410 owns that shape, and the per-M replay below shows no selection change there anymore.
  2. Description: rewritten. It now describes the padded-M walk (exact M → getPaddedM level 0 → level 1), lists the rows that lose M values, and includes the full per-M selection table for main vs this head (M = 1..32768, merged family). The 10 retuned rows have --run_config evidence. For the other 56, the exact tuner commit and flags weren't recorded, and the description says so.
  3. N=1536, K=128 Triton rows: by recorded time, M=24, M=40 and M=56 look slower than what main picks. I haven't run a back-to-back A/B. If you prefer, we can drop those three rows so those M values fall back to the existing rows.

Untuned lists: they now hold exactly the (M, N, K) of the tuned rows this PR adds.

--splitK: every a8w8 row has splitK=0. We'll confirm whether the tuning run had --splitK enabled, and retune with it if you'd like.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@waqahmed-amd-fi

Copy link
Copy Markdown
Contributor

Re your comment, checked at 77eb4f9 (static; nothing was run):

  1. N=6144, K=7168: Confirmed; no row is left there, and the lookup picks main's row at every M.
  2. Description: Confirmed; replaying both lookups reproduces your per-M table exactly, also after the merge of main, whose new rows in these families are gfx1250. One count is off: the diff adds 32 bf16 rows (24 new, 8 replacing main's), so 64 rows lack a --run_config replay, not 56.
  3. N=1536, K=128 Triton rows: Only dropping M=40 (line 760) brings back main's kernel. triton_gemm ignores the row (tuned_gemm.py:758-777), so M=56 makes the same Triton call either way and is not a slowdown, and without line 758, M=24 falls to line 759's Triton row. So M=17–32 follow line 759; without an A/B, keeping it or restoring main's FlyDSL row are both fine with me.
  4. Untuned lists: a8w8 matches exactly. bf16 also keeps 11 prefill shapes (lines 418–428) that only have gfx1250 rows, so a gfx950 re-tune from that list would tune them too. Keeping them is fine; the description's "exactly" could mention them. (My earlier "no tuned row" meant no gfx950 row.)
  5. --splitK: Not needed for this PR; a re-tune with it can follow separately.

Nothing here blocks merge from my side.

@waqahmed-amd-fi waqahmed-amd-fi 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.

Review at 77eb4f9 is approved from my side, no blocking issues.

Left to the author: an optional A/B of the N=1536, K=128 Triton rows and three small description fixes.

@aryatsc

aryatsc commented Oct 30, 2026

Copy link
Copy Markdown
Author

Thanks for the re-check and approval. Description updated with your three fixes: 64 rows without a --run_config replay (32 bf16 rows: 24 new, 8 replacing main's), the N=1536 K=128 note now says only the M=40 row changes the selected kernel, and the untuned bf16 list notes the 11 prefill shapes that only have gfx1250 rows. --splitK re-tune can follow separately.

@demonsan demonsan 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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants