HIP: Tune RDNA4 MMQ tiles for Q6_K / Q2_K (gfx1201) - #25587
Closed
qiongz wants to merge 3 commits into
Closed
Conversation
Tune mul_mat_q for AMD Radeon AI PRO R9700 (gfx1201, RDNA4). All changes are gated on RDNA4; non-RDNA4 targets compile to the upstream kernel. Levers: - Tile config: mmq_x_max=64, mmq_y=64, nwarps=4, MMQ_UNROLL=8 (stock WMMA 128/128/8/default). Halves LDS -> 2 waves/CU, doubles dequant unroll. - load_tiles_q6_K hot path: pointer-stride qs walk, optional first-row ql/qh prefetch, packed 6-bit-in-byte LDS staging expanded in vec_dot. - Y-pipe: prefetch the second Y half into registers before load_tiles and copy it back for the second vec_dot, with the syncthreads required between the first vec_dot (reads tile_y) and the y1_reg->tile_y overwrite. Without that barrier cross-warp LDS races corrupt the dot product (abs_err up to 80x, speculative-decoding accept collapses). - Next-kb block-pointer prefetch (Q6_K). - Small-N decode (mmq_x<=16): unroll the k01 loop by 2 so adjacent K-tile A-ldmatrix loads overlap the previous mma (ATT ds_read wait 6.5%->3.1%). Large mmq_x keeps the default unroll (unroll 2 raises VGPR, regresses prefill). - Q6_K epilogue dispatch on mmq_x: small mmq_x<=16 uses the stock mul+add order (bit-identical to upstream, preserves speculative-decoding accept); large mmq_x uses hoisted-sd fmaf (fast, ~5e-5 fused-rounding residual harmless for prefill). fmaf's 1-rounding vs mul+add's 2 is the residual source; mul+add is bit-exact but ~32% slower on the prefill hot loop, so it is gated to the decode/verify path. Micro-bench (mmq-bench, Q6_K, vs stock WMMA): ffn_gate N14 +12%, N16 +14%, N24 +38%, N32 +35%; ffn_down N14 +23%, N32 +46%; attn_q N14 +5%; gdn N14 +22%; ffn_gate N512 +44%, N2048 +39%. E2E (Qwen3.6-27B-UD-Q6_K_XL, llama-bench + server draft-mtp n-max=2): pp512 705->876 (+24%), pp2048 687->853 (+24%), pp8192 636->775 (+22%); tg128 20.8 (flat, N=1 uses MMVQ); MTP decode 45.7 t/s, accept 91%. Precision: small-N (mmq_x<=16) bit-identical to upstream (ref_abs=0, argmax 100%); large-N within 5e-5 (fmaf, prefill path).
The RDNA4 64/64/4 tile + A6 + UNROLL8 is optimal only for HBM-bound Q6_K (low arithmetic intensity, dequant-heavy). Compute-bound types (Q4_K/Q4_0/ Q5_0/Q8_0, high AI) regressed -12..-23% because nwarps=4/mmq_y=64 cuts MMA throughput. Gate the RDNA4-special geometry on type==Q6_K; all other types fall through to stock 128/128/8. A6 y1-reg-prefetch is gated on type != Q4_K (helps all prefill by avoiding a second HBM read of y1, but Q4_K's scale-heavy dequant hits an occupancy cliff from y1_reg[] pressure at mmq_x=128). N13 next-kb0 prefetch stays Q6_K-only. mmq-bench (Qwen3.6-27B shapes, ffn_gate 17408x5120, vs stock, same-GPU A/B): Q6_K N=512 +44.6%, N=2048 +41.4% (HBM-bound, keeps target) Q4_K N=512 +0.2%, N=2048 +0.1% Q4_0 N=512 +1.1%, N=2048 +1.4% Q5_K N=512 +6.3%, N=2048 +6.8% Q5_0 N=512 +3.3%, N=2048 +3.7% Q8_0 N=512 +1.2%, N=2048 -0.1% (noise) All types >= stock, no regression. Q6_K decode N=14 +9.2%, N=32 +37%. Precision (vs stock golden, --check-ref): Q4/Q5/Q8 bit-exact (ref_abs=0, ref_cos=1.0, argmax=1.0); Q6_K decode bit-exact, prefill 3.8e-5 fmaf residual (accept-irrelevant, gated to mmq_x>16 by the epilogue dispatch). E2E (Qwen3.6-27B-UD-Q6_K_XL, llama-server draft-mtp n-max=2, max_tokens=96 x3, temp=0, two-GPU A/B): accept 91.0% stock == 91.0% patched (identical); t/s 45.5 == 45.6. MTP decode path (mmq_x=16) uses the stock mul+add epilogue => bit-exact => accept unchanged. Bound-class (rocprof ATT): Q6_K wait=30.4%/WMMA=4.6% (HBM-bound, 64/64/4+A6 optimal); Q8_0 wait=17.2%/WMMA=11.8% (compute-bound, stock 128/128/8 optimal). All gates are RDNA4-guarded; non-RDNA4 compiles to upstream stock. Only mmq.cuh + mmq.cu touched (mmvq.cu/vecdotq.cuh N3 decode patch separate).
Tighten the A6 y1-reg-prefetch gate from type != Q4_K to also exclude Q3_K and Q2_K, and cap Q2_K mmq_x_max at 64 (vs stock 128). Q3_K (6-bit scales) hit the same occupancy cliff as Q4_K from y1_reg[] pressure at mmq_x=128, regressing prefill -4.6%. Excluding it restores prefill to stock (+0.2%). Q2_K (2-bit, dequant-bound) had a pre-existing N=128 decode -70% regression: mmq_x=128 gives only 136 blocks and A6 halves occupancy, forcing a badly underutilized 2nd wave. mmq_x_max=64 doubles the block count and alone lifts Q2_K prefill +237% (stock 128 was suboptimal for the low-AI 2-bit kernel). A6 is also excluded - it adds net overhead at both 128 and 64; the gain comes entirely from the tile-size change. mmq-bench (ffn_gate 17408x5120, same-GPU0 A/B): Q2_K N=512 +237%, N=2048 +214%; decode N=128 -0.2% (was -70%) Q3_K N=512 +0.2%, N=2048 +1.1%; decode flat All other types unchanged (Q6_K +43%, Q5_K +6.6%, etc.) Precision: bit-exact vs stock same-session. test-backend-ops 1134/1134 pass. All gates RDNA4-guarded; non-RDNA4 compiles to upstream stock. Assisted-by: Cursor
Contributor
|
Any arch-specific tuning needs to be rebased on top of #24127 . |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR aims to tune mul_mat_q on RDNA4 (gfx1201). Changes are gated with #if defined(RDNA4). Non-RDNA4 builds match upstream. Benchmarked on Radeon AI PRO R9700 / ROCm 7.2.4; shapes from Qwen3.6-27B FFN.
Main Results
mmq-bench prefill ( ffn_gate 17408x5120, same-GPU0 A/B)
27B Q6_K prefill (Qwen3.6-27B-UD-Q6_K_XL)
27B Q2_K prefill (Qwen3.6-27B-UD-Q2_K_XL)
llama-bench 7B Q6_K (Qwen2.5-7B-Instruct-Q6_K)
Decode
E2E MTP accept(Qwen3.6-27B-UD-Q6_K_XL)
Q2_K/Q3_K decode regression fix
Q2_K/Q3_K decode (N=14/32/128): no regression vs stock. Q2_K N=128 was -70% with intermediate build, fixed to -0.2% in final commit.
Perplexity Test
llama-perplexity 7B (wiki.test.raw): stock 7.9666, opt 7.9675 (+0.011%, limit 0.5%)
Test-backend-ops
test-backend-ops MUL_MAT: 1134/1134 passed (ROCm0 backend)
Additional information
Precision (vs stock golden, --check-ref)
Summary: 27/32 BIT-EXACT, 5/32 NEAR (cos=1.0, argmax=1.0, abs<1e-4). No FAIL.
NEAR causes: Q6_K N>=128 = fmaf epilogue; Q2_K N>=512 = mmq_x=64 vs stock 128 reduction order.
Final mmq.cuh state (what's gated on type, RDNA4 only)
Levers to optimize this kernel (what changed)
Commit 1 (ff4c006): Q6_K tuning
Commit 2 (9f07b0d): per-type geometry dispatch
Commit 3 (fe2df7f): Q3_K + Q2_K fix
Requirements