ggml-cuda: pad GEMM weight rows to avoid cache-set aliasing#34
Draft
roberteg16 wants to merge 1 commit into
Draft
ggml-cuda: pad GEMM weight rows to avoid cache-set aliasing#34roberteg16 wants to merge 1 commit into
roberteg16 wants to merge 1 commit into
Conversation
When a GEMM weight's packed row size is a multiple of 2048 bytes, consecutive rows collide in the same last-level cache sets, throttling the weight-bound matmuls. Pad such a weight's row stride by one 128-byte cache line so the rows no longer alias, speeding up F16/BF16 prefill and decode. Enabled by default; set GGML_CUDA_NO_PAD_WEIGHTS to restore the previous layout. The model loader tags mul_mat / mul_mat_id weights with GGML_TENSOR_FLAG_PAD_ROWS and uploads their rows with a strided 2D copy; the CUDA backend reserves and applies the padded nb[1] and feeds the strided weight to cuBLAS via lda without repacking. Quantized weights are excluded, since a 128-byte pad is not a multiple of their block size and would misalign the block-indexed kernels. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
da2a657 to
0fff7d6
Compare
mgehre-amd
reviewed
Jul 6, 2026
| return ctx->dev_ptr; | ||
| } | ||
|
|
||
| // One cache line, added to a weight's row stride when the packed row size is a multiple of |
There was a problem hiding this comment.
I guess this needs some gfx11 qualification as this is not generally applicable to all CUDA GPUs
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
When a GEMM weight's row size in bytes is a multiple of 2048, consecutive rows can collide in the same last-level cache sets, throttling the weight-memory-bound matmuls. This pads such a weight's row stride by one 128-byte cache line, breaking the aliasing. It is enabled by default and can be disabled with
GGML_CUDA_NO_PAD_WEIGHTS. It is a pure memory-layout change: the same values are stored at a padded stride and every consuming kernel is fed that stride, so results are unchanged within floating-point tolerance. The change lives in the generic CUDA/HIP backend and is not tied to any GPU family; how much it helps depends on the device's cache geometry.What changed
ggml.h: addGGML_TENSOR_FLAG_PAD_ROWS, a backend hint marking a GEMM weight whose row stride may be padded.llama-model-loader.cpp: tag weights whose intended op isGGML_OP_MUL_MAT/GGML_OP_MUL_MAT_ID(viallm_tensor_info_for), and upload their rows with a strided 2D copy so packed on-disk data lands in the padded layout.ggml-cuda.cu: reserve the padded allocation and set the paddednb[1..3]in the buffer allocator/initializer; make the cuBLAS path pass the strided weight through un-repacked and uselda = nb[1] / type_size; skip quantized weights, whose block size a 128-byte pad would misalign.common.cuh: addggml_cuda_is_contiguous_rows, shared by the cuBLAS and vector-kernel paths.mmvf.cu: honor the padded row stride in the split-path callback.Scope
Only float tensors used as a GEMM
src0are padded (attention q/k/v/o, ffn gate/up/down, output); embeddings (get_rows), norms, and biases are never touched. Quantized weights are explicitly excluded because a 128-byte pad is not a multiple of their block size (144/176/210 bytes, etc.) and would misalign the block-indexed matmul kernels. The feature is enabled by default; settingGGML_CUDA_NO_PAD_WEIGHTSrestores the previous byte-for-byte layout.Performance (Radeon 8060S,
-fa 1, f16 KV; on = default, off =GGML_CUDA_NO_PAD_WEIGHTS)Models are F16/BF16 because the optimization only affects float weights whose row size in bytes is a multiple of 2048; it has no effect on Q4_K_M (or any other k-quant) models, whose block layout can never form a 2048-byte row, so those configurations run completely unchanged. Since the benchmark dashboard runs its llama.cpp models as Q4_K_M, the numbers below use F16/BF16 builds to exercise the paths this change touches. Qwen3-1.7B and Qwen3-8B are F16 builds of dashboard model families; Llama-3.2-1B, Gemma-3-1B, and Phi-4-mini (BF16) add architecture coverage including the bf16 GEMM path; OLMoE-1B-7B exercises the MoE
mul_mat_idpath; and Gemma-3-1B doubles as a no-op control whose K dimensions never hit the alias.Prefill speedup (prompt tok/s, on vs off) across shapes:
Larger dense model (Qwen3-8B F16): pp512 +44%, pp2048 +37%, pp4096 +35%; TTFT (pp4096 @ depth 4096) +22% faster; decode +5%.
MoE (OLMoE-1B-7B F16, exercises mul_mat_id): pp512 +78%, pp2048 +75%.
TTFT (pp4096 prefill latency, on faster than off) at increasing context depth:
Decode (tg128 tok/s, on vs off): Qwen3-1.7B +11% (depth 0) / +9% (depth 4096); Llama-3.2-1B +9% at both.
Gemma-3-1B is flat because its K dimensions (n_embd 1152, n_ff 6912) are not multiples of 1024, so no F16 row hits the 2048-byte alias and almost nothing is padded; it is a control confirming the change is a no-op when the aliasing condition is not met.
Correctness
Perplexity is identical with padding on (default) vs off: Qwen3-1.7B 1.0043 == 1.0043 and Qwen3-8B 1.0024 == 1.0024 and Llama-3.2-1B 1.0162 == 1.0162 (dense, mul_mat), and OLMoE-1B-7B 1.0093 == 1.0093 (MoE, mul_mat_id). Deterministic greedy output matches across qwen3, gemma3, and phi3 (bf16); Llama occasionally flips a single near-tie token, which is the expected effect of cuBLAS selecting a different tiling for the new leading dimension, not a correctness regression. Quantized (Q4_K) models are a verified no-op.
Test plan
-DGGML_HIP=ON -DGPU_TARGETS=gfx1151 -DCMAKE_BUILD_TYPE=Release.mul_matcorrectness:llama-perplexityon dense F16 models is identical on (default) vsGGML_CUDA_NO_PAD_WEIGHTS(Qwen3-1.7B, Llama-3.2-1B).mul_mat_idcorrectness:llama-perplexityon an F16 MoE is identical on vs off (OLMoE-1B-7B).llama-benchprefill/TTFT/decode show the gain on F16/BF16 models; a Q4_K model is unchanged.