Repository navigation
[Perf] [AMD] batch-invariant: Tune the bf16 matmul for gfx950 - #44045
Conversation
Deterministic inference runs every linear layer through the persistent batch-invariant Triton matmul, with one bf16 launch configuration for all GPUs. On gfx950 a 128x64x128 tile with three stages and 16-wide MFMA instructions is faster. The configuration is still fixed for every M, so batch invariance is unchanged. Other GPUs keep the existing configuration. Signed-off-by: Aarne Talman <aarne.talman@amd.com>
Build the persistent matmul's launch configurations in a small helper and add a CPU test: on gfx950 the bf16 entry must be the measured preset and fit gfx950's 160 KB of LDS, and on other GPUs it must stay the original configuration. A change to the preset now has to be deliberate. Signed-off-by: Aarne Talman <aarne.talman@amd.com>
HaiShaw
left a comment
There was a problem hiding this comment.
How to justify the tuning pertain to Qwen3-8B and Qwen3.8-27B to be generic in a common code path?
|
@HaiShaw Good question. I checked the preset on 19 layer shapes it was not tuned on (Llama 3.1 8B and 70B TP8, Phi-4, gpt-oss-20b, Qwen3-30B-A3B, Qwen3-8B TP8, three LM heads), M = 1-8192, against the default config on MI350X:
The gain is structural: at decode the output has N / BLOCK_N tiles, and the default 128-wide N tile leaves most of MI350X's 256 CUs idle; the preset's 64-wide tile doubles them. So it holds for any layer up to N ~ 8k (1.58-1.89x at decode). It does not help very wide layers, which already fill the GPU: Llama 3.1 8B gate_up (N = 28,672) 0.94x, LM heads 0.97-1.02x. Every shape stayed batch-invariant, and the preset only applies to bf16 on gfx950. |
The preset was tuned on Qwen3-8B and Qwen3.8-27B shapes but measured faster on other models' linear layers too (decode 1.5-1.9x up to N ~ 8k, neutral on wider N). Say that instead of naming the two models. Signed-off-by: Aarne Talman <aarne.talman@amd.com>
|
/rerun-failed-ci |
1 similar comment
|
/rerun-failed-ci |
|
/rerun-failed-ci |
|
All pr-test-base passed. |
| NUM_SMS = get_device_core_count() | ||
|
|
||
| # Use fixed kernel configuration for determinism | ||
| configs = { |
There was a problem hiding this comment.
Hi, @aarnetalman
bmm_batch_invariant still hardcodes the same config. Should it use _matmul_persistent_configs() as well?
There was a problem hiding this comment.
Good question. I measured it, and the matmul preset isn't a straight win for bmm. Its main deterministic-mode user is MLA attention, whose two batched matmuls have very different shapes from the linear layers the preset was tuned on. On MI350X, here is the preset against the current bmm config:
| bmm shape (heads x K x N) | M 1-128 | M 1,024 | M 4,096 |
|---|---|---|---|
| 16 x 512 x 128 | 1.44-1.51x faster | 1.39x | 0.97x |
| 128 x 512 x 128 | 1.09-1.36x faster | 0.99x | 0.87x |
| 16 x 128 x 512 | 0.99-1.01x | 0.86x | 0.80x |
| 128 x 128 x 512 | 0.75-0.83x (slower) | 0.78x | 0.75x |
Both configs are batch-invariant: every row is bitwise the same at every M. But the gains and losses depend on K and N, so switching bmm wholesale would slow the K=128, N=512 matmul.
I'd keep this PR to the 2D matmul it was tuned and measured on, and do bmm as a follow-up. There, the config would be picked by the bmm's own shape (K and N, never M, so it stays batch-invariant), with end-to-end numbers on an MLA model. Does that work for you?
There was a problem hiding this comment.
Sounds great.
Thanks for clarification.
1am9trash
left a comment
There was a problem hiding this comment.
LGTM.
Only config change on gfx950.
Motivation
This PR speeds up deterministic inference on MI350X/MI355X (gfx950): about 6% more throughput with 1k-token inputs and about 1.5% with 8k-token inputs. It gives the batch-invariant bf16 matmul a launch configuration tuned for gfx950.
With
--enable-deterministic-inference, every linear layer goes through the persistent batch-invariant Triton matmul (_matmul_persistent_triton). That kernel uses one bf16 configuration on every GPU (128x128x64 tiles, 3 stages, 8 warps), with no AMD-specific options. On gfx950, a 128x64x128 tile with 16-wide MFMA instructions is up to 2x faster at decode batch sizes.The configuration is still fixed for every M, so each output element keeps the same reduction order whatever the batch: batch invariance is unchanged.
Modifications
batch_invariant_ops.py: on gfx950 only, the bf16 entry becomesBLOCK_SIZE_M=128, BLOCK_SIZE_N=64, BLOCK_SIZE_K=128, GROUP_SIZE_M=8, num_stages=3, num_warps=8, matrix_instr_nonkdim=16.aten::mm/addmm) runs this kernel on ROCm, so the change applies to any bf16 model. Quantized layers, MoE experts (Triton MoE runner) and attention don't use it._matmul_persistent_configs(), and a new CPU test (test/registered/unit/batch_invariant_ops/test_matmul_persistent_configs.py,base-a-test-cpu) guards the preset: on gfx950 the bf16 entry must be exactly this configuration and fit gfx950's 160 KB of LDS, and on other GPUs it must stay the original one.Accuracy Tests
MI350X,
rocm/sgl-dev:v0.5.21-rocm724-mi35x-20261012with this change applied, Qwen3-8B BF16, TP1,--attention-backend triton --enable-deterministic-inference,SGLANG_USE_AITER=0:python3 -m sglang.test.test_deterministic --test-mode prefix --return-logproband--test-mode radix_cache(50 trials): pass.test/registered/unit/batch_invariant_ops/test_batch_invariant_ops.py: 6 passed, 54 subtests, before and after (also with the helper refactor).test_matmul_persistent_configs.py: 2 passed (2 failed on the code before this PR, which has no helper).Speed Tests and Profiling
End to end, deterministic mode as above,
python3 -m sglang.benchmark.serving --backend sglang --dataset-name random --random-range-ratio 1.0 --request-rate inf --seed 1, median of 3 runs each:Kernel, speed-up over the current configuration (
triton.testing.do_bench, weights passed transposed asF.lineardoes):The shapes are the linear layers of Qwen3-8B and Qwen3.8-27B. The geomean is 1.59x at M <= 64 and 0.99x at M >= 1024. A per-(N, K) configuration could recover the prefill shapes later; the configuration must never depend on M.
Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): ✅ Run #40028266705
Latest PR Test (Extra): ❌ Run #40028266638
Latest PR Test (AMD ROCm 10): ❌ Run #40028266701