Skip to content

mmq config for Q4_K/Q5_K/Q6_K - #3

Open
mixa3607 wants to merge 2 commits into
mxxm-t:masterfrom
mixa3607:gfx906-mmq-overrides
Open

mmq config for Q4_K/Q5_K/Q6_K #3
mixa3607 wants to merge 2 commits into
mxxm-t:masterfrom
mixa3607:gfx906-mmq-overrides

Conversation

@mixa3607

@mixa3607 mixa3607 commented Aug 7, 2026

Copy link
Copy Markdown

I personally builded, double-checked, and confirmed that the numbers are true and that the AI ​​made no errors in the research. I am ready to provide all the raw data.

The data in this spreadsheet https://docs.google.com/spreadsheets/d/1gUs7c80ErwtZPrry1sAHvgsGcHsWLk3eHZkZ8hvokjA/edit?gid=373998456#gid=373998456 was collected by me personally, without the involvement of an AI.

Prebuilt images:

  • docker.io/mixa3607/llama.cpp-gfx906:b10254-rocm-6.3.3-mxxm-stock-7af929b-pre
  • docker.io/mixa3607/llama.cpp-gfx906:b10254-rocm-7.14-mxxm-stock-7af929b-pre
  • docker.io/mixa3607/llama.cpp-gfx906:b10254-rocm-7.14-mxxm-patch-7af929b-pre

q6k-rocprof files:

Overview

Add per-format MMQ tile configurations for the K-quant types on gfx906
(Vega20 / MI50, wave64) in ggml/src/ggml-cuda/mmq-config-gfx906.cuh.

The mx-llama.cpp gfx906 config already handles Q8_0 (512 threads, J up to 128)
and MXFP4 (512 threads). This change adds the missing K-quant tuning measured
on MI50 with ROCm 7.14 and -mllvm -amdgpu-sched-strategy=max-ilp.

Additional information

Change

In ggml/src/ggml-cuda/mmq-config-gfx906.cuh, before the rdna2 fallback:

CASE(GGML_TYPE_Q4_K, 256, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, true);
CASE(GGML_TYPE_Q4_K, 256, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

CASE(GGML_TYPE_Q5_K, 256, 2,  64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, true);
CASE(GGML_TYPE_Q5_K, 256, 2,  64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

CASE(GGML_TYPE_Q6_K, 256, 2,  64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q6_K, MMQ_ITER_K, false, true);
CASE(GGML_TYPE_Q6_K, 256, 2,  64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q6_K, MMQ_ITER_K, false, false);

Same thread count, occupancy, J, K and stream-k as the rdna2 table. Only I
changes: I=128 -> I=64 for Q5_K and Q6_K. Q4_K stays at I=128.

Motivation / measured effect

Direct A/B on the mx-llama.cpp tree (751b611), same compiler flag, one MI50,
--n-prompt 2048 --ubatch-size 2048 --batch-size 2048 --n-gen 256,
--n-depth 0 --split-mode layer --flash-attn on.

End-to-end (tokens per second)

First the production GGUFs, then a full per-type matrix.

Config Qwen3.5-9B Q8_0 PP Gemma-4-26B-A4B Q4_K_L PP
mxxm stock (all K = I=128) 1007.89 1365.99
+ these CASE overrides 1006.80 (-0.1%) 1428.77 (+4.6%)

Qwen PP is dominated by Q8_0 and is therefore unchanged. Gemma PP, which is
dominated by Q4_K/Q5_K/Q6_K, improves by 4.6%. TG is unchanged within noise.

Full per-type matrix (PP, same bench settings; models quantized from the same
F16 source with the mxxm llama-quantize):

Type Qwen stock Qwen +case delta Gemma stock Gemma +case delta
Q4_K_S 906.99 915.19 +0.9% 1429.40 1432.62 +0.2%
Q5_K_S 552.38 659.87 +19.5% 1005.20 1150.46 +14.4%
Q6_K 545.05 766.48 +40.6% 1072.31 1397.02 +30.3%
Q4_0 1127.98 1126.19 -0.2% - - -
Q4_1 1118.29 1116.14 -0.2% - - -
Q5_0 566.68 566.10 -0.1% - - -
Q5_1 553.88 554.53 +0.1% - - -
Q8_0 1005.19 1004.49 -0.1% 1622.31 1626.25 +0.2%
IQ4_XS 586.08 601.09 +2.6% - - -
IQ4_NL 595.38 612.42 +2.9% - - -

Non-K types are untouched, as expected. The K-quant gains reproduce on both a
9B dense model and a 26B MoE, and scale with model size.

Kernel totals (Gemma PP, fallback=false)

Kernel stock +override delta
Q4_K, J=64 1.959 s 1.986 s +1.4% (noise)
Q5_K, J=64 1.105 s 0.931 s -15.7%
Q6_K, J=64 0.631 s 0.410 s -35.0%

I=64 halves the accumulator array (32 -> 16 floats per thread) and roughly
halves the X tile in LDS. On gfx906 this removes register pressure/scratch for
Q5_K and Q6_K without hurting their tile reuse. Q4_K was measured to lose ~53%
at I=64, so it keeps I=128.

Verification

  • MUL_MAT: 1186/1186 passed.
  • MUL_MAT_ID: 865/865 passed (MoE expert boundaries).
  • Bit-exact: the change only alters tile geometry/thread mapping, not the
    accumulation math; all backend comparisons pass.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: YES - Almost all of the research was conducted by AI

@mxxm-t
mxxm-t force-pushed the master branch 4 times, most recently from b39391d to a03ff66 Compare August 10, 2026 07:00
@mxxm-t

mxxm-t commented Aug 14, 2026

Copy link
Copy Markdown
Owner

OK I ran tests and short summary:

measurement yours base
Q6_K kernel time, J=64 -35.0% -37.1% dense, -46.1% MoE matches
pp, Q4_K + Q6_K mixed file +4.6% (Q4_K_L) +6.7% (Q4_K_M) matches
pp, Q4_K dominant +0.9% / +0.2% +0.1% matches
Q4_K kernel time, J=64 +1.4% (noise) -0.2% matches
pp, Q8_0 -0.1% untouched by construction matches
tg unchanged flat across 9 model x topology cells matches
MUL_MAT / MUL_MAT_ID tests all passed 11/11 and 3/3 on q5_K, q6_K matches
pp, Q5_K dominant +19.5% / +14.4% -5.0 to -5.3% does not match
Q5_K kernel time, J=64 -15.7% +5.7% slower does not match
bit-exact yes yes for Q6_K, no for Q5_K does not match
cuts VGPR / scratch yes nothing spills either way on a stock build does not match

//
// Q4_K/Q5_K/Q6_K: measured on MI50. I=64 halves the accumulator array and the X
// LDS tile, cutting VGPR/scratch and improving Q5_K/Q6_K throughput ~15-35%.
// Q4_K stays at I=128 (I=64 makes it ~53% slower). J=64 only.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

"cutting VGPR/scratch" does not hold on a stock build - J=64 comes out 85 VGPRs against 84 with ScratchSize 0 in both, Q5_K and Q6_K alike. Only occupancy moves from 2 to 3.

}
CASE(GGML_TYPE_Q4_K, 256, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, true);
CASE(GGML_TYPE_Q4_K, 256, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

These are probably not needed as rdna2 already returns the same nine parameters for Q4_K?

CASE(GGML_TYPE_Q4_K, 256, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

CASE(GGML_TYPE_Q5_K, 256, 2, 64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, true);
CASE(GGML_TYPE_Q5_K, 256, 2, 64, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

These cost -9.6% on current base build, kernel 8002 -> 8461 us, but gain +3.1% under max-ilp.
Would nthreads be the knob instead here? 512 at the inherited I=128 gives 956.5 vs 610.9 for I=64 here, still not bit exact.

    CASE(GGML_TYPE_Q5_K, 512, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, true);
    CASE(GGML_TYPE_Q5_K, 512, 2, 128, 64, GGML_CUDA_MMQ_SRAM_LAYOUT_Q8_1, MMQ_ITER_K, false, false);

@mixa3607

Copy link
Copy Markdown
Author

Hi. Sorry for the long reply, I've been busy.
If that's okay with you, could you add any patches you find useful and close my MR?

I'm (and ai) can try to look for something else and prepare better, but it’s possible that the same crap will happen again.

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