Skip to content

CUDA: size routed MoE MMQ N-tiles from typical expert width on RDNA3 - #24546

Open
ravel7524 wants to merge 5 commits into
ggml-org:masterfrom
ravel7524:mmqx-rdna3-routed-moe-tiling
Open

CUDA: size routed MoE MMQ N-tiles from typical expert width on RDNA3#24546
ravel7524 wants to merge 5 commits into
ggml-org:masterfrom
ravel7524:mmqx-rdna3-routed-moe-tiling

Conversation

@ravel7524

@ravel7524 ravel7524 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR improves routed MoE prefill performance on RDNA3 by changing how llama.cpp chooses MMQ N-tile sizes. Instead of using the worst case expert width, it estimates the typical routed expert width and uses that when it is smaller than the RDNA3 maximum tile width. The launch grid still uses the original worst case width, so output coverage and correctness are preserved. The change is limited to host side tile selection in mul_mat_q_case and does not modify structs, call sites, launch parameters, or device kernels. Benchmarks on a Radeon Pro W7800/gfx1100 with ROCm 7.2.3 show large prefill gains in many routed MoE cases, while decode and non RDNA3 architectures are mostly unchanged.

If maintainers or other contributors have access to NVIDIA, CDNA, RDNA2, or RDNA4 hardware, the natural follow up would be to test the same routed width picker under the corresponding MMQ/launch policy and enable it per architecture where it is beneficial.

Benchmark Summary

Hardware/software:

  • AMD Radeon Pro W7800, gfx1100 / RDNA3
  • ROCm 7.2.3
  • HIP backend, -ngl 99
  • GGML_CUDA_FA_ALL_QUANTS=ON
  • -fa 1 -ctk q8_0 -ctv f16 -sm none -t 4 --poll 0 -r 5

The improvement depends on the physical ubatch/chunk width that reaches routed MoE MMQ, not total context length:

win when:          ncols_typical < mmq_x_max
crossover_ubatch: 128 * n_experts / n_expert_used

So long prompts can still benefit when they are processed in smaller ubatch chunks.

Benchmark sweep
model / quant experts ubatch pp512 pp1024 pp2048 pp4096 pp8192 tg128
Qwen3.6-35B-A3B Q6_K 256 2048 +48.46% +22.15% +7.57% n/a n/a -0.67%
Qwen3-30B-A3B Q6_K 128 512 +35.50% +33.79% +32.15% n/a n/a -1.11%
Qwen3-30B-A3B Q6_K 128 2048 +36.62% +13.90% -0.75% n/a n/a -0.89%
Qwen3-30B-A3B Q4_K_M 128 512 +33.68% +31.54% +29.81% +27.70% +23.86% +0.35%
Qwen3-30B-A3B Q4_K_M 128 2048 +33.77% +14.00% -0.08% -0.06% -0.01% -0.20%
Qwen3.6-35B-A3B Q3_K_M 256 512 +23.48% +22.37% +22.73% +22.91% +21.18% -0.08%
Qwen3.6-35B-A3B Q3_K_M 256 2048 +22.93% +6.99% +4.02% +3.92% +3.69% +0.15%
Gemma-4-26B-A4B Q4_K_M 128 512 +29.53% +28.52% +26.65% +25.46% +23.19% +0.04%
Gemma-4-26B-A4B Q4_K_M 128 2048 +29.87% +13.16% +0.03% +0.05% +0.04% -0.09%
Gemma-4-26B-A4B Q6_K 128 512 +25.05% +24.05% +23.21% +22.51% +20.59% +0.08%
Gemma-4-26B-A4B Q6_K 128 2048 +25.53% +11.75% +0.11% -0.04% -0.00% +0.50%

The 128 expert models show the expected crossover behavior at ubatch 2048: pp2048 and larger are neutral because the typical width has reached mmq_x_max = 128. The 256-expert Qwen3.6 shape remains below crossover and still improves through pp8192.

Requirements

  • Yes, I have read and agree with the contributing guidelines
  • AI usage disclosure: YES, AI was used to help review the change and summarize benchmark results. I reviewed the final diff and can explain all code changes.

@github-actions github-actions Bot added Nvidia GPU Issues specific to Nvidia GPUs ggml changes relating to the ggml tensor library for machine learning labels Jun 12, 2026
@ravel7524
ravel7524 marked this pull request as ready for review June 12, 2026 21:04
@ravel7524
ravel7524 requested a review from a team as a code owner June 12, 2026 21:04
@ravel7524

Copy link
Copy Markdown
Contributor Author

Here is an additional sweep extending the prior pp8192 table upward. Values are MMQX vs base throughput deltas. tg128 is included as the decode control.

Hardware/software: AMD Radeon Pro W7800 (gfx1100), ROCm 7.2.3, HIP backend, -ngl 99, GGML_CUDA_FA_ALL_QUANTS=ON, -fa 1 -ctk q8_0 -ctv f16 -sm none -t 4 --poll 0.

model / quant experts ubatch pp8192 pp16384 pp32768 pp65536 tg128 largest clean
Qwen3-30B-A3B Q4_K_M 128 512 1661.44 -> 2049.97 (+23.39%) 1349.44 -> 1599.57 (+18.54%) 990.08 -> 1118.48 (+12.97%) 633.79 -> 682.82 (+7.74%) 133.61 -> 134.08 (+0.35%) pp65536
Qwen3-30B-A3B Q4_K_M 128 1024 2043.18 -> 2243.77 (+9.82%) 1602.36 -> 1726.49 (+7.75%) 1121.08 -> 1179.00 (+5.17%) 691.16 -> 711.71 (+2.97%) 130.59 -> 131.20 (+0.46%) pp65536
Qwen3-30B-A3B Q4_K_M 128 2048 2265.11 -> 2259.45 (-0.25%) 1730.70 -> 1731.02 (+0.02%) 1178.53 -> 1177.80 (-0.06%) 718.44 -> 718.40 (-0.00%) 130.73 -> 130.73 (+0.00%) pp65536
Qwen3-30B-A3B Q4_K_M 128 4096 2312.65 -> 2308.85 (-0.16%) 1758.76 -> 1757.65 (-0.06%) 1192.18 -> 1192.16 (-0.00%) 720.98 -> 720.85 (-0.02%) 129.29 -> 129.89 (+0.46%) pp65536
Qwen3-30B-A3B Q4_K_M 128 8192 2087.19 -> 2150.76 (+3.16%) 1675.31 -> 1674.92 (-0.02%) 1154.52 -> 1154.02 (-0.04%) 705.68 -> 705.59 (-0.01%) 128.90 -> 129.74 (+0.65%) pp65536
Gemma-4-26B-A4B Q4_K_M 128 512 1849.80 -> 2279.65 (+23.24%) 1662.29 -> 2004.19 (+20.57%) 1401.96 -> 1623.81 (+15.83%) 1054.25 -> 1171.73 (+11.14%) 85.08 -> 85.02 (-0.06%) pp65536
Gemma-4-26B-A4B Q4_K_M 128 1024 2278.15 -> 2502.82 (+9.86%) 1990.69 -> 2164.03 (+8.71%) 1617.03 -> 1722.56 (+6.53%) 1169.98 -> 1224.99 (+4.70%) 78.59 -> 84.29 (+7.84%) pp65536
Gemma-4-26B-A4B Q4_K_M 128 2048 2474.42 -> 2467.24 (-0.29%) 2124.07 -> 2124.74 (+0.03%) 1702.03 -> 1702.09 (+0.00%) 1213.12 -> 1211.99 (-0.09%) 78.47 -> 84.25 (+7.98%) pp65536
Gemma-4-26B-A4B Q4_K_M 128 4096 2448.56 -> 2442.26 (-0.26%) 2070.54 -> 2069.72 (-0.04%) 1668.67 -> 1668.29 (-0.02%) 1193.49 -> 1195.54 (+0.17%) 78.54 -> 84.19 (+7.79%) pp65536
Gemma-4-26B-A4B Q4_K_M 128 8192 2122.56 -> 2118.94 (-0.17%) 1854.58 -> 1854.11 (-0.03%) 1510.94 -> 1511.74 (+0.05%) 1110.49 -> 1108.28 (-0.20%) 77.39 -> 84.41 (+9.99%) pp65536
Gemma-4-26B-A4B Q6_K 128 512 1605.25 -> 1935.92 (+20.60%) 1460.33 -> 1733.39 (+18.70%) 1267.12 -> 1444.12 (+13.97%) 983.37 -> 1086.60 (+10.50%) 88.47 -> 88.33 (-0.15%) pp65536
Gemma-4-26B-A4B Q6_K 128 1024 2046.42 -> 2238.64 (+9.39%) 1811.34 -> 1966.32 (+8.56%) 1503.42 -> 1603.36 (+6.65%) 1117.89 -> 1171.41 (+4.79%) 86.96 -> 87.59 (+0.73%) pp65536
Gemma-4-26B-A4B Q6_K 128 2048 2283.96 -> 2275.69 (-0.36%) 1981.77 -> 1982.07 (+0.01%) 1617.79 -> 1616.75 (-0.06%) 1180.25 -> 1180.33 (+0.01%) 87.59 -> 87.60 (+0.01%) pp65536
Gemma-4-26B-A4B Q6_K 128 4096 2301.54 -> 2293.49 (-0.35%) 1963.89 -> 1961.98 (-0.10%) 1608.37 -> 1608.08 (-0.02%) 1175.85 -> 1175.53 (-0.03%) 87.41 -> 87.65 (+0.27%) pp65536
Gemma-4-26B-A4B Q6_K 128 8192 2028.64 -> 2020.28 (-0.41%) 1780.78 -> 1784.42 (+0.21%) 1470.22 -> 1468.89 (-0.09%) 1089.65 -> 1088.48 (-0.11%) 87.63 -> 87.72 (+0.10%) pp65536

Across the tested 128 expert models, the gain persists at long context for smaller ubatches, and the larger ubatches are effectively neutral rather than regressing. I can do additional long context benchmarking for 256 expert models

@github-actions github-actions Bot added the CUDA Related to the CUDA backend label Jun 19, 2026
@ravel7524

Copy link
Copy Markdown
Contributor Author

@jiachengjason would you be able to benchmark this PR on your RDNA4 GPU when you have time? Since you tested #23685 on RDNA4, having the same kind of validation here would be very helpful.

If you know anyone who could also test on RDNA2 or CDNA, that would be great as well. No pressure.

@ravel7524

Copy link
Copy Markdown
Contributor Author

@IMbackK when you have time, could you sanity check whether this RDNA3 routed MoE MMQ selector direction looks reasonable? The PR is scoped to gfx1100/RDNA3 and uses measured W7800 results; I mainly want to know if the shape is acceptable or if you would prefer a different architecture gating/config style.

@GZGavinZhao

Copy link
Copy Markdown
Contributor

Do you expect this to work well on RDNA3.5 / Strix Halo as well? Happy to test!

@ravel7524

Copy link
Copy Markdown
Contributor Author

Yes, the PR’s RDNA3 predicate also includes RDNA3.5 / Strix Halo, so the routed-width selector should be active there. I expect the same general direction, although the performance magnitude may differ from gfx1100. Testing would be very helpful, ideally the same base vs PR prefill and decode comparison used in the PR.

@IMbackK IMbackK self-assigned this Jul 7, 2026
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Jul 17, 2026
…24546)

Upstream PR ggml-org#24546 (ravel7524, open) sizes routed-MoE MMQ N-tiles from the
typical expert width instead of the worst case. That is the same problem the
static J_max=48 cap from Findings ggml-org#8/ggml-org#9 solves, approached from the other end:
the cap clamps the search ceiling, the picker fixes the search objective. This
commit swaps the cap for the picker so the two can be A/B'd on gfx1151.

The PR does not apply as written - it patches mmq_x/mmq_x_max/
mmq_get_granularity_host in mul_mat_q_case, all deleted by PR ggml-org#24127, the same
refactor that forced the Finding ggml-org#5 -> ggml-org#9 re-port. Re-authored onto
mul_mat_q_switch_J's config-table search and scoped to RDNA3.5: gfx1151 is the
only arch we can measure, and narrowing the predicate keeps the A/B
single-variable. Upstream's predicate already covers RDNA3.5 via
GGML_CUDA_CC_IS_RDNA3, and the author confirmed on 2026-07-04 that Strix Halo
should benefit, but nobody has ever tested it there.

On the routed-MoE path ncols_max is ne12 - the worst case of one expert
receiving every token - so the search never reaches ntiles==1 and simply runs
to whatever ceiling it is given. The picker instead tiles from
ncols_dst/nchannels_x, the width a typical expert covers. launch_mul_mat_q
still builds the grid from ncols_max, so worst-case coverage is unchanged.

  - mmq.cuh: replace the RDNA3.5 MoE J_max=48 clamp with the typical-width
    picker; J_max is now a uniform 128 and the search terminates on its own.
  - mmq-table-check.cpp: pick_J mirrors the selection loop, so update it in
    lockstep or the check silently becomes a rubber stamp. Adds the routed-MoE
    arg shape from mmq.cu and a crossover case.

For Qwen 3.6 35B-A3B (256 experts, 8 active) at the production ub=2048 the
typical width is 2048*8/256 = 64, so this is a 48 -> 64 change at our operating
point - narrow enough that it may land in noise. Verified host-side without
ROCm: MoE J=64, dense J=128, no aborts across all 21 types.

Above ub=4096 the typical width reaches 128, the picker disengages and the
search runs to J=128 where the cap held 48. Findings ggml-org#5/ggml-org#8 only ever measured
the typical=64 regime, so that is untested on this chip; production ub=2048
cannot reach it. See strix-halo/mmq-moe-ncols-picker.md for the bench plan and
the keep/revert criteria.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Jul 17, 2026
Benched b47bb31 against 05e837f on gfx1151 (ROCm 7.14.0, canonical Qwen 3.6
35B-A3B Q4_K_XL matrix). The picker is not distinguishable from the static cap
at the production operating point:

  pp512  @ d=0      1428.13 -> 1388.93  (-2.7%)
  pp512  @ d=2048   1299.39 -> 1316.26  (+1.3%)
  pp512  @ d=8192   1135.42 -> 1142.23  (+0.6%)
  pp512  @ d=16384   971.25 ->  977.36  (+0.6%)

Correctness was clean first (790/790 MUL_MAT_ID, 1134/1134 MUL_MAT), so the
grid does still cover ncols_max while the tile is sized from the typical width.

The tg128 control fell ~1.6% at every depth. This change cannot move tg -
decode goes through MMVQ where ncols_dst==1, so the picker never runs - which
makes that a session drift between two builds measured on different days, the
same contamination Finding ggml-org#9 hit. The conclusion survives it either way: raw,
pp is -2.7% to +1.3%; calibrated against the drift, -1.2% to +2.9%. Every depth
is inside the host's ~2% noise floor, so the effect is smaller than this rig can
resolve, and the d=0 -2.7% is no more real than the d=2048 +1.3%.

So: at ub=2048, J=48 and J=64 are the same speed. The doc predicted this before
the numbers existed ("narrow enough that it may well land in noise"), which is
the only reason that reading is credible rather than post-hoc. Reverting to the
static cap because it is the validated status quo, not because it won.

Worth reporting on ggml-org#24546: the picker is neutral on RDNA3.5,
on the exact model and ubatch its sweep claims +7.57% for on gfx1100. The PR is
gated on GGML_CUDA_CC_IS_RDNA3, which covers both. gfx1151's table is I=64/
nthreads=128 vs gfx1100's I=128/nthreads=256 - our tiles are already half-width,
so there is much less worst-case over-sizing left to recover. That is the data
ravel7524 asked for on 2026-07-03.

Doc kept with the numbers and the untested ub>=4096 crossover regime recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Jul 22, 2026
…24546)

Upstream PR ggml-org#24546 (ravel7524, open) sizes routed-MoE MMQ N-tiles from the
typical expert width instead of the worst case. That is the same problem the
static J_max=48 cap from Findings ggml-org#8/ggml-org#9 solves, approached from the other end:
the cap clamps the search ceiling, the picker fixes the search objective. This
commit swaps the cap for the picker so the two can be A/B'd on gfx1151.

The PR does not apply as written - it patches mmq_x/mmq_x_max/
mmq_get_granularity_host in mul_mat_q_case, all deleted by PR ggml-org#24127, the same
refactor that forced the Finding ggml-org#5 -> ggml-org#9 re-port. Re-authored onto
mul_mat_q_switch_J's config-table search and scoped to RDNA3.5: gfx1151 is the
only arch we can measure, and narrowing the predicate keeps the A/B
single-variable. Upstream's predicate already covers RDNA3.5 via
GGML_CUDA_CC_IS_RDNA3, and the author confirmed on 2026-07-04 that Strix Halo
should benefit, but nobody has ever tested it there.

On the routed-MoE path ncols_max is ne12 - the worst case of one expert
receiving every token - so the search never reaches ntiles==1 and simply runs
to whatever ceiling it is given. The picker instead tiles from
ncols_dst/nchannels_x, the width a typical expert covers. launch_mul_mat_q
still builds the grid from ncols_max, so worst-case coverage is unchanged.

  - mmq.cuh: replace the RDNA3.5 MoE J_max=48 clamp with the typical-width
    picker; J_max is now a uniform 128 and the search terminates on its own.
  - mmq-table-check.cpp: pick_J mirrors the selection loop, so update it in
    lockstep or the check silently becomes a rubber stamp. Adds the routed-MoE
    arg shape from mmq.cu and a crossover case.

For Qwen 3.6 35B-A3B (256 experts, 8 active) at the production ub=2048 the
typical width is 2048*8/256 = 64, so this is a 48 -> 64 change at our operating
point - narrow enough that it may land in noise. Verified host-side without
ROCm: MoE J=64, dense J=128, no aborts across all 21 types.

Above ub=4096 the typical width reaches 128, the picker disengages and the
search runs to J=128 where the cap held 48. Findings ggml-org#5/ggml-org#8 only ever measured
the typical=64 regime, so that is untested on this chip; production ub=2048
cannot reach it. See strix-halo/mmq-moe-ncols-picker.md for the bench plan and
the keep/revert criteria.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Jul 22, 2026
Benched b47bb31 against 05e837f on gfx1151 (ROCm 7.14.0, canonical Qwen 3.6
35B-A3B Q4_K_XL matrix). The picker is not distinguishable from the static cap
at the production operating point:

  pp512  @ d=0      1428.13 -> 1388.93  (-2.7%)
  pp512  @ d=2048   1299.39 -> 1316.26  (+1.3%)
  pp512  @ d=8192   1135.42 -> 1142.23  (+0.6%)
  pp512  @ d=16384   971.25 ->  977.36  (+0.6%)

Correctness was clean first (790/790 MUL_MAT_ID, 1134/1134 MUL_MAT), so the
grid does still cover ncols_max while the tile is sized from the typical width.

The tg128 control fell ~1.6% at every depth. This change cannot move tg -
decode goes through MMVQ where ncols_dst==1, so the picker never runs - which
makes that a session drift between two builds measured on different days, the
same contamination Finding ggml-org#9 hit. The conclusion survives it either way: raw,
pp is -2.7% to +1.3%; calibrated against the drift, -1.2% to +2.9%. Every depth
is inside the host's ~2% noise floor, so the effect is smaller than this rig can
resolve, and the d=0 -2.7% is no more real than the d=2048 +1.3%.

So: at ub=2048, J=48 and J=64 are the same speed. The doc predicted this before
the numbers existed ("narrow enough that it may well land in noise"), which is
the only reason that reading is credible rather than post-hoc. Reverting to the
static cap because it is the validated status quo, not because it won.

Worth reporting on ggml-org#24546: the picker is neutral on RDNA3.5,
on the exact model and ubatch its sweep claims +7.57% for on gfx1100. The PR is
gated on GGML_CUDA_CC_IS_RDNA3, which covers both. gfx1151's table is I=64/
nthreads=128 vs gfx1100's I=128/nthreads=256 - our tiles are already half-width,
so there is much less worst-case over-sizing left to recover. That is the data
ravel7524 asked for on 2026-07-03.

Doc kept with the numbers and the untested ub>=4096 crossover regime recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Aug 3, 2026
…24546)

Upstream PR ggml-org#24546 (ravel7524, open) sizes routed-MoE MMQ N-tiles from the
typical expert width instead of the worst case. That is the same problem the
static J_max=48 cap from Findings ggml-org#8/ggml-org#9 solves, approached from the other end:
the cap clamps the search ceiling, the picker fixes the search objective. This
commit swaps the cap for the picker so the two can be A/B'd on gfx1151.

The PR does not apply as written - it patches mmq_x/mmq_x_max/
mmq_get_granularity_host in mul_mat_q_case, all deleted by PR ggml-org#24127, the same
refactor that forced the Finding ggml-org#5 -> ggml-org#9 re-port. Re-authored onto
mul_mat_q_switch_J's config-table search and scoped to RDNA3.5: gfx1151 is the
only arch we can measure, and narrowing the predicate keeps the A/B
single-variable. Upstream's predicate already covers RDNA3.5 via
GGML_CUDA_CC_IS_RDNA3, and the author confirmed on 2026-07-04 that Strix Halo
should benefit, but nobody has ever tested it there.

On the routed-MoE path ncols_max is ne12 - the worst case of one expert
receiving every token - so the search never reaches ntiles==1 and simply runs
to whatever ceiling it is given. The picker instead tiles from
ncols_dst/nchannels_x, the width a typical expert covers. launch_mul_mat_q
still builds the grid from ncols_max, so worst-case coverage is unchanged.

  - mmq.cuh: replace the RDNA3.5 MoE J_max=48 clamp with the typical-width
    picker; J_max is now a uniform 128 and the search terminates on its own.
  - mmq-table-check.cpp: pick_J mirrors the selection loop, so update it in
    lockstep or the check silently becomes a rubber stamp. Adds the routed-MoE
    arg shape from mmq.cu and a crossover case.

For Qwen 3.6 35B-A3B (256 experts, 8 active) at the production ub=2048 the
typical width is 2048*8/256 = 64, so this is a 48 -> 64 change at our operating
point - narrow enough that it may land in noise. Verified host-side without
ROCm: MoE J=64, dense J=128, no aborts across all 21 types.

Above ub=4096 the typical width reaches 128, the picker disengages and the
search runs to J=128 where the cap held 48. Findings ggml-org#5/ggml-org#8 only ever measured
the typical=64 regime, so that is untested on this chip; production ub=2048
cannot reach it. See strix-halo/mmq-moe-ncols-picker.md for the bench plan and
the keep/revert criteria.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
justinappler added a commit to justinappler/llama.cpp-strix-halo that referenced this pull request Aug 3, 2026
Benched b47bb31 against 05e837f on gfx1151 (ROCm 7.14.0, canonical Qwen 3.6
35B-A3B Q4_K_XL matrix). The picker is not distinguishable from the static cap
at the production operating point:

  pp512  @ d=0      1428.13 -> 1388.93  (-2.7%)
  pp512  @ d=2048   1299.39 -> 1316.26  (+1.3%)
  pp512  @ d=8192   1135.42 -> 1142.23  (+0.6%)
  pp512  @ d=16384   971.25 ->  977.36  (+0.6%)

Correctness was clean first (790/790 MUL_MAT_ID, 1134/1134 MUL_MAT), so the
grid does still cover ncols_max while the tile is sized from the typical width.

The tg128 control fell ~1.6% at every depth. This change cannot move tg -
decode goes through MMVQ where ncols_dst==1, so the picker never runs - which
makes that a session drift between two builds measured on different days, the
same contamination Finding ggml-org#9 hit. The conclusion survives it either way: raw,
pp is -2.7% to +1.3%; calibrated against the drift, -1.2% to +2.9%. Every depth
is inside the host's ~2% noise floor, so the effect is smaller than this rig can
resolve, and the d=0 -2.7% is no more real than the d=2048 +1.3%.

So: at ub=2048, J=48 and J=64 are the same speed. The doc predicted this before
the numbers existed ("narrow enough that it may well land in noise"), which is
the only reason that reading is credible rather than post-hoc. Reverting to the
static cap because it is the validated status quo, not because it won.

Worth reporting on ggml-org#24546: the picker is neutral on RDNA3.5,
on the exact model and ubatch its sweep claims +7.57% for on gfx1100. The PR is
gated on GGML_CUDA_CC_IS_RDNA3, which covers both. gfx1151's table is I=64/
nthreads=128 vs gfx1100's I=128/nthreads=256 - our tiles are already half-width,
so there is much less worst-case over-sizing left to recover. That is the data
ravel7524 asked for on 2026-07-03.

Doc kept with the numbers and the untested ub>=4096 crossover regime recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@IMbackK

IMbackK commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

I think this is a reasonable idea, but it would have to be updated for the changes in #24127

@ravel7524
ravel7524 force-pushed the mmqx-rdna3-routed-moe-tiling branch from 9ab2b07 to 3087cea Compare August 5, 2026 18:48
@ravel7524

Copy link
Copy Markdown
Contributor Author

I adapted the implementation to the MMQ configuration changes from #24127 and I provisionally enabled the selector for NVIDIA Volta and newer so those architectures can be benchmarked.

Comment thread ggml/src/ggml-cuda/mmq.cuh Outdated
GGML_CUDA_CC_IS_RDNA2(cc) ||
GGML_CUDA_CC_IS_RDNA3(cc) ||
GGML_CUDA_CC_IS_RDNA4(cc);
}

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.

Instead of this, please enroll this tunable in the ggml_cuda_mmq_config table introduced in the linked pr

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok just Updated the PR is this how you want it ?

@IMbackK

IMbackK commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

I tested it on CDNA 1 and it seams that its not entirely a win in all cases.

Model Test Profile b10288 t/s PR t/s Delta (%)
qwen35moe 122B.A10B Q4_K - Medium pp32 244.15 ± 2.58 291.14 ± 1.43 +19.25%
pp512 649.31 ± 3.81 733.93 ± 3.05 +13.03%
pp2048 1128.57 ± 7.95 1245.77 ± 2.55 +10.39%
tg32 52.26 ± 0.78 52.69 ± 0.87 +0.82%
deepseek2 30B.A3B Q8_0 pp32 402.87 ± 3.37 441.00 ± 8.24 +9.46%
pp512 1508.40 ± 5.74 1494.33 ± 4.37 -0.93%
pp2048 1327.65 ± 2.52 1308.98 ± 2.13 -1.41%
tg32 84.63 ± 1.21 86.51 ± 1.28 +2.22%
deepseek2 30B.A3B Q8_0 pp32 @ d16384 212.65 ± 1.43 223.26 ± 1.42 +4.99%
pp512 @ d16384 334.95 ± 0.46 323.71 ± 28.41 -3.36%
pp2048 @ d16384 315.96 ± 9.35 315.71 ± 9.81 -0.08%
tg32 @ d16384 56.34 ± 0.64 57.04 ± 0.63 +1.24%
gpt-oss 120B MXFP4 MoE pp32 @ d16384 327.72 ± 5.80 369.25 ± 3.87 +12.67%
pp512 @ d16384 912.97 ± 5.61 831.88 ± 0.86 -8.88%
pp2048 @ d16384 1257.41 ± 178.26 1183.48 ± 174.00 -5.88%
tg32 @ d16384 99.70 ± 0.73 99.47 ± 0.86 -0.23%

IMbackK
IMbackK previously approved these changes Aug 6, 2026

@IMbackK IMbackK 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.

Dont bother cleaning this up if its not uniformly performance positive at least on rdna3

Comment thread ggml/src/ggml-cuda/mmq.cuh Outdated
@IMbackK
IMbackK self-requested a review August 6, 2026 19:15
@IMbackK
IMbackK dismissed their stale review August 6, 2026 19:16

missclick

@ravel7524

ravel7524 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Benchmark Sweep, on newest changes RDNA3/ W7800:

Model pp32 pp512 pp2048 tg128
Qwen3 Q4_K_M +18.4% +41.2% +9.2% +0.0%
Qwen3 Q6_K +25.3% +47.9% +11.3% +0.1%
Gemma-4 Q4_K_M +12.0% +31.6% +7.5% +0.1%
GPT-OSS-20B MXFP4 +61.22% +15.36% -0.13% +0.04%
Benchmark Sweep Detailed Results
model / quant experts ubatch pp32 pp512 pp2048 tg128
Qwen3-30B-A3B Q4_K_M 128 512 750.29 → 885.10 (+17.97%) 2254.61 → 3185.67 (+41.30%) 2165.73 → 2941.54 (+35.82%) 121.76 → 121.88 (+0.10%)
Qwen3-30B-A3B Q4_K_M 128 1024 740.66 → 881.97 (+19.08%) 2253.66 → 3171.83 (+40.74%) 2886.53 → 3190.47 (+10.53%) 121.88 → 121.06 (-0.67%)
Qwen3-30B-A3B Q4_K_M 128 2048 748.18 → 886.73 (+18.52%) 2260.84 → 3196.40 (+41.38%) 3336.16 → 3334.83 (-0.04%) 121.98 → 121.86 (-0.10%)
Qwen3-30B-A3B Q4_K_M 128 4096 749.04 → 885.34 (+18.20%) 2263.06 → 3196.72 (+41.26%) 3341.55 → 3336.38 (-0.15%) 121.98 → 122.31 (+0.27%)
Qwen3-30B-A3B Q4_K_M 128 8192 750.83 → 886.40 (+18.06%) 2261.53 → 3198.49 (+41.43%) 3339.57 → 3336.80 (-0.08%) 121.61 → 122.14 (+0.44%)
Qwen3-30B-A3B Q6_K 128 512 545.90 → 683.34 (+25.18%) 1647.52 → 2438.95 (+48.04%) 1613.77 → 2315.13 (+43.46%) 113.10 → 113.09 (-0.01%)
Qwen3-30B-A3B Q6_K 128 1024 543.72 → 680.54 (+25.16%) 1646.20 → 2434.88 (+47.91%) 2187.29 → 2478.30 (+13.30%) 113.10 → 113.20 (+0.09%)
Qwen3-30B-A3B Q6_K 128 2048 542.12 → 680.40 (+25.51%) 1648.83 → 2433.64 (+47.60%) 2571.58 → 2564.78 (-0.26%) 113.16 → 113.04 (-0.11%)
Qwen3-30B-A3B Q6_K 128 4096 541.87 → 679.33 (+25.37%) 1647.35 → 2433.87 (+47.74%) 2569.99 → 2568.53 (-0.06%) 113.02 → 113.30 (+0.25%)
Qwen3-30B-A3B Q6_K 128 8192 542.72 → 679.76 (+25.25%) 1647.08 → 2438.65 (+48.06%) 2573.13 → 2569.03 (-0.16%) 112.79 → 113.15 (+0.32%)
Gemma-4-26B-A4B Q4_K_M 128 512 809.18 → 904.14 (+11.74%) 2490.26 → 3281.13 (+31.76%) 2309.95 → 2975.25 (+28.80%) 81.25 → 81.28 (+0.04%)
Gemma-4-26B-A4B Q4_K_M 128 1024 807.27 → 902.93 (+11.85%) 2490.39 → 3270.97 (+31.34%) 2971.52 → 3233.85 (+8.83%) 81.25 → 81.31 (+0.07%)
Gemma-4-26B-A4B Q4_K_M 128 2048 804.89 → 901.77 (+12.04%) 2491.49 → 3278.43 (+31.59%) 3392.14 → 3392.00 (-0.00%) 81.28 → 81.27 (-0.01%)
Gemma-4-26B-A4B Q4_K_M 128 4096 804.85 → 900.41 (+11.87%) 2497.58 → 3276.32 (+31.18%) 3392.95 → 3393.67 (+0.02%) 81.30 → 81.36 (+0.07%)
Gemma-4-26B-A4B Q4_K_M 128 8192 803.49 → 902.69 (+12.35%) 2488.03 → 3280.99 (+31.87%) 3391.70 → 3390.98 (-0.02%) 81.23 → 81.32 (+0.11%)
model / quant experts ubatch pp32 pp512 pp2048 pp4096 pp8192 tg128
GPT-OSS-20B MXFP4 32 512 675.86 → 1090.69 (+61.38%) 3073.23 → 3554.54 (+15.66%) 3030.63 → 3459.81 (+14.16%) 2961.45 → 3360.29 (+13.47%) 2779.89 → 3159.47 (+13.65%) 153.25 → 152.71 (-0.35%)
GPT-OSS-20B MXFP4 32 1024 675.03 → 1086.78 (+61.00%) 3075.03 → 3540.49 (+15.14%) 3785.67 → 3785.62 (-0.00%) 3652.23 → 3649.81 (-0.07%) 3423.43 → 3430.36 (+0.20%) 152.39 → 152.86 (+0.31%)
GPT-OSS-20B MXFP4 32 2048 675.56 → 1089.15 (+61.22%) 3070.27 → 3541.97 (+15.36%) 4203.84 → 4198.34 (-0.13%) 4043.86 → 4047.45 (+0.09%) 3777.14 → 3785.89 (+0.23%) 152.79 → 152.85 (+0.04%)
GPT-OSS-20B MXFP4 32 4096 674.61 → 1086.68 (+61.08%) 3067.52 → 3545.21 (+15.57%) 4203.34 → 4197.44 (-0.14%) 3907.50 → 3916.61 (+0.23%) 3717.17 → 3725.87 (+0.23%) 153.06 → 152.85 (-0.14%)
GPT-OSS-20B MXFP4 32 8192 674.24 → 1089.19 (+61.54%) 3071.16 → 3539.03 (+15.23%) 4204.77 → 4200.36 (-0.10%) 3903.64 → 3914.40 (+0.28%) 3255.13 → 3253.76 (-0.04%) 153.15 → 153.03 (-0.08%)

@IMbackK

IMbackK commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Thanks, ill give it another sweep myself on rdna, other than that i think this is solid now.

baramofme added a commit to baramofme/llama-cpp-turboquant that referenced this pull request Aug 15, 2026
- ggml/src/ggml-hip: Adding CDNA, RDNA2 and RDNA4 architecture support
- ggml/src/ggml-hip: Indentation cleanup
- ggml/src/ggml: adjust ncols_picker for routed MoE in mul_mat_q_case
- server: fix reasoning budget WebUI precedence over model.ini

Assisted-by: Sisyphus
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CUDA Related to the CUDA backend ggml changes relating to the ggml tensor library for machine learning Nvidia GPU Issues specific to Nvidia GPUs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants