ggml-cuda: one-time K-padded f16 dequantization for prefill GEMMs#37
Draft
roberteg16 wants to merge 1 commit into
Draft
ggml-cuda: one-time K-padded f16 dequantization for prefill GEMMs#37roberteg16 wants to merge 1 commit into
roberteg16 wants to merge 1 commit into
Conversation
Add an opt-in GGML_PREFILL_DEQUANT=<min_tokens> path that, for large-batch (prefill) quantized dense GEMMs, routes src0 through the dequantize-to-f16 + hipBLAS path instead of MMQ. The weight is dequantized ONCE into a persistent, K-padded (+1 cache line, avoids cache-set aliasing) f16 buffer cached by weight pointer; subsequent calls reuse it, so only the per-call activation convert and the GEMM run. Decode (small batch) is untouched and stays on MMQ/MMVQ. On gfx1151 the padded f16 hipBLAS kernel reaches ~80% of the f16 WMMA roofline vs MMQ's ~61%; combined with ROCBLAS_USE_HIPBLASLT=1 this is a small prefill win. Off by default. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
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.
Summary
This PR adds an opt-in path that, for large-batch (prefill) quantized dense matmuls, dequantizes the weight to f16 once and runs the GEMM through hipBLAS instead of MMQ. The weight is dequantized a single time into a persistent, K-padded f16 buffer that is cached and reused across calls, so subsequent prefill steps pay only the activation conversion and the GEMM itself. Decode (small batch) is left entirely on the existing MMQ/MMVQ path. The feature is off by default and enabled with the
GGML_PREFILL_DEQUANTenvironment variable.Motivation
On RDNA3.5 (gfx1151) the dense q8_0 attention projections in prefill run through MMQ (int8 WMMA) at roughly 61% of the f16 WMMA roofline. Roofline profiling and standalone hipBLAS microbenchmarks showed that the same GEMM, run as f16 through hipBLAS with a cache-line-padded K dimension, reaches roughly 80% of that roofline and is faster at the kernel level for batch sizes at and above ~512 tokens. The catch is that a naive dequantize-on-every-call approach loses this advantage because the per-call weight dequantization (q8_0 to f16) costs more than the GEMM saves. This PR removes that tax by dequantizing each weight exactly once and caching the padded f16 result.
How it works
Two changes, both confined to
ggml/src/ggml-cuda/ggml-cuda.cu. First, inggml_cuda_mul_mat, whenGGML_PREFILL_DEQUANT=<min_tokens>is set and the matmul is a quantized weight withsrc1->ne[1] >= min_tokens(default 32), MMQ and MMVQ are disabled for that op so dispatch falls through toggml_cuda_op_mul_mat_cublas. Second, inggml_cuda_op_mul_mat_cublas, the f16 branch dequantizes a quantized src0 once into a persistent buffer whose row stride is padded by one cache line (GGML_CUDA_CACHE_LINE), which avoids the cache-set aliasing that otherwise halves hipBLAS throughput when the packed row size is a multiple ofGGML_CUDA_ROW_ALIAS_STRIDE. The padded buffer is stored in a static map keyed by the weight device pointer; on a cache hit the stored pointer and padded leading dimension are used directly and no dequantization is performed. The GEMM leading dimension (lda) is switched from the packedne00to the padded stride for both the COMPUTE_32F and COMPUTE_16F code paths.Performance
Measured on gfx1151 (Strix Halo, Qwen3.6-35B-A3B UD-Q4_K_M) with the roofline profiler and llama-bench. At the kernel level the padded f16 hipBLAS GEMM for the largest dense projection (q8_0 [2048, 8192], M=512) reaches about 34.7 TFLOPS (80.7% of the 43 TFLOPS f16 WMMA roofline) versus MMQ at about 26 TFLOPS (61%). The pure-prefill throughput improvement is roughly 3-5% at pp512 and above when combined with
ROCBLAS_USE_HIPBLASLT=1. End-to-end (combined prefill plus decode) the gain is small and workload-dependent: decode is unaffected and dilutes the prefill improvement, batch sizes at or below 128 tokens are below the crossover where MMQ is still faster, and the dense projections are only a fraction of prefill time since the MoE expert GEMMs remain on MMQ. On this thermally noisy APU the e2e delta at pp128+tg128 and pp4096+tg128 is within run-to-run variance. The feature is therefore most useful for prefill-heavy, low-generation workloads (long-context prompt processing, summarization, batch scoring).Usage
Enable at runtime with
GGML_PREFILL_DEQUANT=32(the value is the minimum token count that triggers the path; any value greater than 1 is used verbatim, otherwise it defaults to 32). Pair it withROCBLAS_USE_HIPBLASLT=1so the underlyingcublasGemmExcall is served by hipBLASLt, whose default heuristic picks a good f32-accumulate WMMA kernel for this case; without it rocBLAS may select a slower solution. When the variable is unset the code path is identical to before.Correctness
The padded f16 layout is bit-identical to the unpadded f16 layout (the padding only changes the row stride, not the data), and the f16 GEMM result matches MMQ within quantization error (about 1e-3 relative). Greedy generation output was verified to match the baseline for the cases tested.