Skip to content

[Triton/Gluon] Kimi-K3 GEMM tuning, caller-owned outputs and large-M bpreshuffle reuse - #6262

Closed
yuychang wants to merge 1 commit into
ROCm:mainfrom
yuychang:k3/gemm-out-reuse-tuning
Closed

yuychang wants to merge 1 commit into
ROCm:mainfrom
yuychang:k3/gemm-out-reuse-tuning

Conversation

@yuychang

@yuychang yuychang commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Kimi-K3 runs FP8 bpreshuffle, BF16 and MXFP4 GEMM shapes on gfx950 that have no tuned config, so they use default kernels. FP8 calls with M above the tuned table also use the default CK kernel. Some callers need the GEMM to write into a buffer they own.

Technical Details

  • gemm_a8w8_bpreshuffle and gemm_a4w4 accept an optional out= tensor. The shape, dtype and device are checked.
    • For gemm_a4w4, out must be [ceil(M/32)*32, N], because the kernels write whole 32-row tiles. The result is the first M rows. out= is not supported on gfx1250.
  • When M is above every tuned M for an (N, K), gemm_a8w8_bpreshuffle reuses the row with the largest tuned M. This only applies up to 3x that M; larger M use the default kernel. A row tuned for a small M became slower than the default kernel at much larger M.
    • M values between tuned rows still use the default kernel.
    • FlyDSL launches are split into chunks of 65536 rows (PRESHUFFLE_M_MAX).
  • Tuned configs (gfx950, 256 CU):
    • a8w8_bpreshuffle_tuned_gemm_kimik3.csv: 87 rows for N=7168 K=3584, N=6336 K=7168, N=2112 K=7168 and N=6144 K=7168.
    • kimik3_bf16_tuned_gemm.csv: 217 rows.
    • Triton JSONs for gemm_afp4wfp4 and gemm_a16wfp4 at N=1536 K=7168 and N=7168 K=768. M buckets that were slower than DEFAULT.json use the default config.
  • 9 rows for N=2112 K=7168 (M=1..128) are removed from dsv3_a8w8_bpreshuffle_tuned_gemm.csv. The new Kimi-K3 rows have the same keys and a lower tuned time. Keeping both would make the config merge fail with a duplicate-shape error.
  • op_tests/test_gemm_a8w8_bpreshuffle_large_m.py tests the reuse path, the chunking, the 3x limit, and that M between tuned rows uses the default kernel.

Test Plan

  • pytest op_tests/test_gemm_a8w8_bpreshuffle_large_m.py
  • python3 -m unittest op_tests.tuning_tests.test_config_shape_collision
  • A/B against upstream/main (80f1165) on one MI355X. The same scripts run on both trees, back to back, for 2 or 3 rounds. Timing uses run_perftest, and each output is checked against a torch reference with checkAllclose. Shapes:
    • gemm_a8w8_bpreshuffle: new rows, M above the table, and the DSv3 N=2112 shapes.
    • bf16 tgemm.mm: new rows.
    • Triton FP4: new JSON shapes, M=1..8192.
  • torch.compile with out= for both GEMMs.

Test Result

Setup: MI355X (gfx950), ROCm 7.2, torch 2.11.0+rocm7.2, Triton 3.7.0, FlyDSL 0.3.4.1. Base is main at 80f1165. Base and head ran back to back on the same GPU with the same script.

Summary, this PR vs main (GPU kernel time, best of 2-3 rounds):

group shapes geomean range
a8w8 bpreshuffle, new rows N=7168 K=3584 (M 1-256) 9 1.35x 1.12x-1.68x
a8w8 bpreshuffle, new rows N=6336 K=7168 (M 1-256) 9 1.32x 1.14x-1.57x
a8w8 bpreshuffle, N=2112 K=7168, the 9 replaced DSv3 rows 9 1.56x 1.14x-2.34x
a8w8 bpreshuffle, new large-M rows N=6144 K=7168 4 1.29x 1.27x-1.34x
a8w8 bpreshuffle, N=7168 K=3584 M 264-768 (reuse path) 4 1.17x 1.06x-1.49x
a8w8 bpreshuffle, N=7168 K=3584 M 1024-16384 (same kernel as main) 8 1.01x 0.99x-1.08x
a8w8 bpreshuffle, chunked large M, N=3072 K=512 (M 70000, 98304) 2 1.38x 1.29x-1.47x
bf16 tgemm.mm, new rows 36 1.06x 0.98x-1.27x
Triton gemm_a16wfp4, M <= 256 16 1.68x 1.01x-3.07x
Triton gemm_a16wfp4, M > 256 8 1.00x 1.00x-1.01x
Triton gemm_afp4wfp4, M <= 256 16 1.13x 0.99x-1.44x
Triton gemm_afp4wfp4, M > 256 8 1.21x 0.98x-2.30x

Shapes between 0.97x and 0.99x run the same kernel or config as main, so those gaps are run-to-run noise.

  • Unit test: 3 passed. Duplicate-shape guard: 21 tests OK, no duplicates in the a8w8 bpreshuffle or bf16 families.
  • All measured outputs match the torch reference.
  • gemm_a8w8_bpreshuffle (head vs base):
    • New rows: 1.12x to 2.41x.
    • DSv3 N=2112 K=7168 M=1..128: 1.14x to 2.34x.
    • N=7168 K=3584 at M=264..768 (reuse path): 1.06x to 1.49x.
    • Chunked large M (N=3072 K=512): 1.29x to 1.47x.
    • M above 3x the table: same kernel as base, 0.98x to 1.03x.
  • bf16 tgemm.mm: geomean 1.06x (0.98x to 1.27x). One tuned row (M=6043 N=7168 K=1536) was removed. M=6043 now matches the M=6086 row, which uses the same kernel.
  • Triton: gemm_a16wfp4 1.01x to 3.07x at M<=256 and 1.00x above. gemm_afp4wfp4 0.98x to 2.30x. Shapes below 1.00x use the same config as base.
  • torch.compile: gemm_a8w8_bpreshuffle with and without out= matches eager. gemm_a4w4 with out= matches eager under aot_eager. Under inductor, gemm_a4w4 fails on upstream/main too, even without out=. That is not caused by this change.

Submission Checklist

- Accept caller-owned outputs in gemm_a8w8_bpreshuffle and gemm_a4w4 through an optional out= argument.
- Above the bpreshuffle table, reuse the largest tuned row up to 3x its M; chunk FlyDSL launches at their row limit.
- Add large-M A8W8 bpreshuffle and KDA prefill tuning with dispatch tests.
- Replace 9 DSv3 N=2112 K=7168 bpreshuffle rows with faster Kimi-K3 rows that have the same keys.
- Add Kimi-K3 BF16 tuning, keeping upstream rows and avoiding duplicate untuned keys.
- Add gfx950 shared-expert FP4 GEMM configs; M buckets that were slower than DEFAULT.json use the default config.
@github-actions

github-actions Bot commented Oct 8, 2026

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: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: GPT-OSS-120B, DeepSeek-R1-0528, Kimi-K2.5
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 6262 --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
yuychang marked this pull request as ready for review October 12, 2026 02:55
@yuychang
yuychang requested review from a team and a balanced review from Copilot October 12, 2026 02:55
@github-actions github-actions Bot changed the title [Config] [FlyDSL] Kimi-K3 GEMM tuning, caller-owned outputs and large-M bpreshuffle reuse [Triton/Gluon] Kimi-K3 GEMM tuning, caller-owned outputs and large-M bpreshuffle reuse Oct 12, 2026

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

The A8W8 caller-owned output accepts unsupported noncontiguous buffers, and new dispatch/API paths lack required coverage.

5 open findings
What changed in this PR

Adds Kimi-K3 GEMM tuning, caller-owned outputs, and large-M FP8 configuration reuse.

Changes:

  • Adds gfx950 BF16, FP8, and FP4 tuning data.
  • Adds optional out= buffers to A8W8 and A4W4 GEMMs.
  • Reuses and chunks large-M FlyDSL configurations.
File Description
op_tests/​test_gemm_a8w8_bpreshuffle_large_m.py Tests large-M reuse and chunking.
aiter/​ops/​triton/​configs/​gfx950/​triton/​gemm/​gemm_afp4wfp4/​GEMM-AFP4WFP4-N=7168-K=768.json Adds AFP4/WFP4 tuning.
aiter/​ops/​triton/​configs/​gfx950/​triton/​gemm/​gemm_afp4wfp4/​GEMM-AFP4WFP4-N=1536-K=7168.json Adds AFP4/WFP4 tuning.
aiter/​ops/​triton/​configs/​gfx950/​triton/​gemm/​gemm_a16wfp4/​GEMM-A16WFP4-N=7168-K=768.json Adds A16/WFP4 tuning.
aiter/​ops/​triton/​configs/​gfx950/​triton/​gemm/​gemm_a16wfp4/​GEMM-A16WFP4-N=1536-K=7168.json Adds A16/WFP4 tuning.
aiter/​ops/​gemm_op_a8w8.py Adds output buffers and large-M reuse.
aiter/​ops/​gemm_op_a4w4.py Adds padded caller-owned outputs.
aiter/​configs/​model_configs/​kimik3_bf16_untuned_gemm.csv Adds BF16 tuning inputs.
aiter/​configs/​model_configs/​kimik3_bf16_tuned_gemm.csv Adds BF16 selected kernels.
aiter/​configs/​model_configs/​dsv3_a8w8_bpreshuffle_tuned_gemm.csv Removes superseded rows.
aiter/​configs/​model_configs/​a8w8_bpreshuffle_untuned_gemm_kimik3.csv Adds FP8 tuning inputs.
aiter/​configs/​model_configs/​a8w8_bpreshuffle_tuned_gemm_kimik3.csv Adds FP8 selected kernels.

🧠 Review effort: Balanced


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

Comment thread aiter/ops/gemm_op_a8w8.py
Comment on lines +902 to +905
if out.shape != (m, n) or out.dtype != dtype or out.device != XQ.device:
raise ValueError(
f"out must be shape {(m, n)}, dtype {dtype}, device {XQ.device}; "
f"got shape {tuple(out.shape)}, dtype {out.dtype}, device {out.device}"
Comment thread aiter/ops/gemm_op_a4w4.py
Comment on lines +239 to +243
m_padded = (m + 31) // 32 * 32
if out is None:
out = torch.empty((m_padded, n), dtype=dtype, device=A.device)
else:
out = _check_gemm_a4w4_out(out, m_padded, n, dtype, A.device)
Comment thread aiter/ops/gemm_op_a8w8.py
Comment on lines +656 to +658
found = rows.get(
_bpreshuffle_group(has_gfx, get_gfx(), get_cu_num(), n, k, q_dtype_w)
)
Comment thread aiter/ops/gemm_op_a8w8.py
bias: Tensor | None = None,
dtype: torch.dtype = dtypes.bf16,
check: bool = False,
out: Tensor | None = None,
Comment thread aiter/ops/gemm_op_a4w4.py
apreshuffle: bool | None = False,
global_A_scale: Tensor | None = None, # NVFP4 per-tensor
global_B_scale: Tensor | None = None, # NVFP4 per-tensor
out: Tensor | None = None, # [ceil(M / 32) * 32, N] caller-owned output
@yuychang

Copy link
Copy Markdown
Contributor Author

Closing in favor of a split, one concern per PR, as suggested in review:

@yuychang yuychang closed this Oct 12, 2026
@yuychang

Copy link
Copy Markdown
Contributor Author

Update on the large-M bpreshuffle reuse: it's dropped, so there won't be a fifth PR.

I swept every gfx950 (N, K) group in the merged table where reuse would run: 43 groups, at 1.5x, 2x and 3x the largest tuned M. Reuse was slower than the default kernel in 9 groups, at every ratio. Examples: DSv3 N=2112 K=7168 at 0.17x, GLM-4.7 N=3072 K=5120 at 0.69x, DSv3 N=3072 K=1536 at 0.83x. Limiting it to gfx950 doesn't help, because the losses are on gfx950 too. Without reuse, the FlyDSL chunking isn't needed either.

Instead, #6410 now has tuned rows for Kimi-K3 N=7168 K=3584 at M=288..1024. Against main, that's 1.05x-1.72x (geomean 1.19x) across M=264..1024, compared with 0.95x-1.46x from the reuse path.

The sweep also turned up an existing issue on main: the FlyDSL 8-wave preshuffle kernel (flydsl_bpreshuffle_8w_*) sometimes returns corrupted rows. One example is the existing GLM-5.2 N=2624 K=6144 M=32768 row: 1 of 3 runs had 428 rows off by up to 84. #6410 adds no 8-wave rows. The N=6144 K=7168 rows from this PR used that kernel and are dropped.

@yuychang
yuychang deleted the k3/gemm-out-reuse-tuning branch October 12, 2026 11:47
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.

2 participants