cuda/hip: optional wave64 for the quantized mat-vec kernels on RDNA - #89
Draft
mgehre-amd wants to merge 1 commit into
Draft
cuda/hip: optional wave64 for the quantized mat-vec kernels on RDNA#89mgehre-amd wants to merge 1 commit into
mgehre-amd wants to merge 1 commit into
Conversation
Adds GGML_HIP_MMVQ_WAVE64, off by default, which builds mmvq.cu with
-mwavefrontsize64. Exploratory: it is a clear win on a couple of shapes and a
clear loss on more of them, so it is exposed as a flag rather than turned on,
and the data is recorded here so the next person does not have to rediscover it.
Not applied target-wide because fattn-mma-f16.cuh static_asserts on wave32
tiling. -mwavefrontsize64 is per translation unit, so this flips all 265 kernels
in mmvq.cu, not only the q4_K one. A function attribute cannot narrow it:
clang refuses to inline any wave32 function into a target("wavefrontsize64")
one, down to the threadIdx accessors, and every kernel here is built from
__forceinline__ helpers. Narrowing it properly needs the q4_K instantiation
moved to its own translation unit.
q4_K, wave32 vs wave64, rocprofv3 kernel time, median of 3 passes, gfx1151:
m=21504 n=1 k=5376 289.6 -> 285.6 us -1.4%
m=4096 n=4 k=14336 117.6 -> 110.0 us -6.5%
m=4096 n=8 k=14336 210.0 -> 223.1 us +6.2%
m=4096 n=4 k=4096 35.1 -> 40.5 us +15.4%
m=4096 n=1 k=4096 32.3 -> 37.5 us +16.2%
m=4096 n=1 k=14336 54.1 -> 69.6 us +28.7%
Only the gemma-4-31B-it fused FFN gate/up shape and one batched shape benefit.
The k=4096 and k=14336 n=1 shapes lose badly. The likely mechanism is the K-loop
trip count: at nwarps=2 and wave64, blocks_per_iter doubles, so k=4096 gives two
iterations instead of four and the 4x unroll has nothing left to pipeline.
Across all 21 mmvq types at m=4096 n=1 k=4096, median of 3: 5 improve by more
than 2% (iq2_xs -12.8, iq2_xxs -12.5, iq3_xxs -12.5, iq2_s -11.2, iq3_s -5.7),
9 regress by more than 2% (q4_K +18.7, iq4_xs +15.5, mxfp4 +9.8, nvfp4 +9.1,
q2_K +7.7, q4_0 +5.4, iq1_s +4.7, iq4_nl +4.4, q5_0 +2.4), 7 are within +/-2%.
In-model impact on gfx1151 is narrower than that table suggests: with
GGML_CUDA_DQ_MMV on by default, q4_K/q5_K/q6_K at ne11==1 go to
mul_mat_vec_dq_*, which this does not touch. The only mmvq user left for
gemma-4-31B-it Q4_K_M is the fused GEGLU gate/up op, which is the 21504x5376
shape above. Models built from non-K quants route every n=1 matvec through
mmvq and would take the regressions.
Verification: test-backend-ops test -o
MUL_MAT,MUL_MAT_ID,MUL_MAT_VEC_FUSION,MUL_MAT_ID_FUSION passes with the flag on,
with and without GGML_CUDA_DQ_MMV=0. Note the flag miscompiles on ROCm
7.12.0a20260211 (clang 22): 371 of 1175 MUL_MAT cases fail, including the
benchmarked shape at ERR 0.47. 7.15.0a20260728 is clean. There is no version
guard; that is a reason to keep this opt-in.
Assisted-by: Claude Opus 5
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.
Adds
GGML_HIP_MMVQ_WAVE64, off by default, which buildsmmvq.cuwith-mwavefrontsize64.@Annieren,
This is exploratory rather than a proposed default. RDNA can pick a wave size per kernel, and for
a couple of shapes wave64 is a clear win - but for more of them it is a clear loss. The point of
the PR is to expose the switch and record the measurements so the tradeoff is visible, rather
than have the next person rediscover it.
21 lines of change.
Which shapes benefit, and which don't
q4_K,
rocprofv3kernel time, median of 3 interleaved passes, gfx1151 / Radeon 8060S,ROCm 7.15.0a20260728,
GGML_CUDA_DQ_MMV=0:Two of six shapes benefit. The one that motivated the work - the gemma-4 fused FFN gate/up matvec
The likely mechanism is the K-loop trip count.
blocks_per_iter = vdr * nwarps * warp_size / qi,so doubling the wave size halves the iteration count. At k=4096 with nwarps=2 that is two
iterations instead of four, and the 4x unroll from the parent PR has nothing left to pipeline.
The shapes that win are the ones with enough K to keep it fed.
Which types benefit, and which don't
All 21 mmvq types at
m=4096 n=1 k=4096, median of 3 passes. Note this is the shape where q4_Kitself does worst, so read it as one slice, not a verdict:
Seven more (q6_K, q4_1, q5_K, q3_K, q8_0, q1_0, q5_1) are within +/-2%. The iq2/iq3 family is the
consistent winner; everything with a cheap
vec_dottends to lose.Why it is a build flag and not something narrower
-mwavefrontsize64is a per-translation-unit flag, so it flips all 265 kernels inmmvq.cu,not just the q4_K one. It is not applied target-wide because
fattn-mma-f16.cuhstatic_asserts onwave32 tiling.
A function attribute cannot narrow it.
__attribute__((target("wavefrontsize64")))does work inisolation - it emits
.wavefront_size: 64for that kernel alone - but clang then refuses toinline any wave32 function into it:
That extends to
threadIdxaccessors, and every kernel here is assembled from__forceinline__helpers, so there is nothing left to attribute. Narrowing this properly means moving the q4_K
instantiation into its own translation unit compiled with the flag, which is a larger change and
is not attempted here.
Verification
test-backend-ops test -o MUL_MAT,MUL_MAT_ID,MUL_MAT_VEC_FUSION,MUL_MAT_ID_FUSION -b ROCm0passes with the flag on, with and without
GGML_CUDA_DQ_MMV=0.Reproducing
Runs were serialised under exclusive GPU access at DPM level high and gated on the GPU edge
sensor at 52 C.