From 85ff4657cb3839809c9bc3cbdd89b7e9445cdc13 Mon Sep 17 00:00:00 2001 From: xrip Date: Mon, 7 Sep 2026 20:24:30 +0300 Subject: [PATCH] cuda: tune the mmvq/mmq crossover for Turing Turing had no entry in ggml_cuda_should_use_mmvq and fell through to the generic ne11 <= 8, while Ada, Blackwell, DGX Spark and Orin all have measured switch points. MMQ overtakes MMVQ far earlier than n=8 on SM75. Measured on a CMP 50HX (cc 7.5, CUDA 13.3) with test-backend-ops perf, medians of 4 reps after a discarded warmup, forcing each path via an instrumentation build. MMQ speedup over MMVQ at n=8: q2_K 2.57x q3_K 2.13x q4_K 1.96x iq4_xs 1.93x q5_K 1.79x q6_K 1.74x End to end on Qwen3.5-9B-UD-Q4_K_XL, llama-bench, medians of 3 interleaved rounds at -r 20: pp1 -0.2% pp2 0.0% pp3 0.0% pp4 +3.2% pp5 +12.1% pp8 +47.6% q6_K is deliberately 3 rather than the 2 the synthetic sweep suggested. That sweep had a single shape for q6_K and MMQ won it by only 1.02x, the weakest margin measured. With the threshold at 2 the model regressed 2% at pp3, reproducibly across three rounds: its 24 q6_K tensors were the only ones switching path at that batch size. The synthetic margin did not survive on the model's real q6_K shapes, so mmvq keeps n=3 for that type. q4_K was measured across five m/k shapes; at n=3 mmvq wins 1.19x/1.09x/1.08x on three and loses 1.01x on two, geometric mean 1.065 in mmvq's favour. At n=4 mmq wins on all five. The n=512 cases act as a control: above MMVQ_MAX_BATCH_SIZE neither mode can select mmvq, and both measure within 1.00x-1.01x of each other. Only the measured types are listed; everything else keeps the default. q2_K and q3_K rest on a single shape each and are not present in the model used for the end-to-end check, so they are less well evidenced than the rest. --- ggml/src/ggml-cuda/mmvq.cu | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ggml/src/ggml-cuda/mmvq.cu b/ggml/src/ggml-cuda/mmvq.cu index 7fba66a3d..d490f14ca 100644 --- a/ggml/src/ggml-cuda/mmvq.cu +++ b/ggml/src/ggml-cuda/mmvq.cu @@ -367,6 +367,25 @@ bool ggml_cuda_should_use_mmvq(enum ggml_type type, int cc, int64_t ne11) { return ne11 <= MMVQ_MAX_BATCH_SIZE; } } + if (GGML_CUDA_CC_IS_NVIDIA(cc) && cc == GGML_CUDA_CC_TURING) { + // Tuned on a CMP 50HX. Turing had no entry here and fell through to + // ne11 <= 8, but MMQ overtakes MMVQ much earlier than that: by n=8 it + // is 1.7x-2.6x faster depending on the type. Only the types measured + // are listed; the rest keep the default. + switch (type) { + case GGML_TYPE_Q2_K: + case GGML_TYPE_Q3_K: + return ne11 <= 2; + case GGML_TYPE_Q4_K: + case GGML_TYPE_Q5_K: + case GGML_TYPE_Q6_K: + case GGML_TYPE_IQ4_XS: + return ne11 <= 3; + default: + return ne11 <= MMVQ_MAX_BATCH_SIZE; + } + } + if (GGML_CUDA_CC_IS_NVIDIA(cc) && cc == GGML_CUDA_CC_VOLTA) { switch (type) { case GGML_TYPE_Q2_K: