Repository navigation
docs: rocBLAS on gfx1151 — both runtime routes closed, and why - #371
Conversation
Follows up the MMQ vs hipBLAS A/B, where I wrote that rocBLAS was worth investigating for better kernels. It was worth investigating; the answer is no. Three findings, in order of how much they change what we believed: 1. These GEMMs never reach should_use_mmq. ggml_cuda_mul_mat bails to cuBLAS at its first branch when dst->type != F32, and every captured call has d_type f16_r. GGML_CUDA_FORCE_CUBLAS cannot move any of them, so the earlier A/B had them on rocBLAS in both arms -- which is why dense gemma4:31b measured +2.1% rather than showing an MMQ win. 2. It is not a vision-tower story. qwen3.8:27b sends 17.0 of its 23.9 TFLOP per image request through the language model, not the tower. 3. Both runtime knobs are closed. ROCBLAS_USE_HIPBLASLT is inert (-0.8%) because neither the ROCm 7.2.4 nor the ROCm 10 image ships a single gfx1151 hipBLASLt kernel file -- packaging, not architecture. Forcing FP32 accumulate halves prefill (-48.6%), so the f16 default is already the fast path by 2x. Adds the captured shape inventory as data, a generator for the tables (ADR 0012 rule 8), and the capture script. The -48.6% figure doubles as a regression sentinel: if prefill on this host ever halves, check compute_type first. Open: whether the 2x from FP16 accumulate costs accuracy (suite running), and whether an F16-output path could skip rocBLAS for the tower shapes entirely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
I asserted that every captured GEMM takes the `dst->type != F32` bailout in ggml_cuda_mul_mat, inferring it from `--d_type f16_r` on every call. The inference is wrong. d_type is set by `prefer_f32_output`, which is false on gfx1151 because RDNA3_5 is RDNA3 and the list only covers VOLTA, RDNA4 and CDNA. So f16 output is what this architecture gets regardless of dst->type, and the log says nothing about which of the two callers ran. That also retracts the conclusion drawn from it -- that FORCE_CUBLAS could not move any of these GEMMs, and that this explained dense gemma4:31b measuring +2.1%. Undetermined until measured. Diffing the rocBLAS inventory between the MMQ and FORCE_CUBLAS builds separates the two callers cleanly; queued. The arch list is itself the more interesting finding and replaces the retracted one: RDNA4 and CDNA get F16 inputs with F32 accumulate, rocBLAS ships HPA kernels for gfx1151, and gfx1151 is excluded. That is a different knob from GGML_CUDA_CUBLAS_COMPUTE_TYPE=f32 (which converts inputs too, and halves throughput) and needs a build to test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Correction — finding #1 retractedPushed in What I claimed: every captured GEMM takes the Why it's wrong: if (compute_type == GGML_TYPE_F16) {
prefer_f32_output = cc == GGML_CUDA_CC_VOLTA || GGML_CUDA_CC_IS_RDNA4(cc) || GGML_CUDA_CC_IS_CDNA(cc);
}gfx1151 is RDNA3_5 — RDNA3, not RDNA4 — so That distinction matters rather than being pedantic: if the LM GEMMs arrive by fallthrough, then How it gets settled: diff the rocBLAS inventory between the MMQ build and the What replaces it — and it's a better findingThe arch list is the real result. RDNA4 and CDNA get F16 inputs with F32 accumulate. rocBLAS ships HPA kernels for gfx1151 ( This is not the same knob as Everything else in the PR body stands: the hipBLASLt packaging finding, the −48.6% compute-type measurement, the shape inventory, and the regression sentinel. |
Measured, not inferred: - Caller split: diffing the rocBLAS inventory between the MMQ and FORCE_CUBLAS builds shows zero MMQ-build shapes missing under FORCE_CUBLAS -- the flag only adds work. So the retracted conclusion (FORCE_CUBLAS cannot move these GEMMs) holds after all, on evidence this time rather than on the invalid d_type inference. - FP32 accumulate: -48.6% throughput and no accuracy gain; the single cell that moved went the wrong way. The f16 default is correct on both axes. - prefer_f32_output for RDNA3.5: -30.2% and -33.3%. Exclusion is correct tuning. Gate-off reproduced production (154.2 vs 154.9), so the build holds. And the finding that reframes the rest: tower attention is ~12.2 TFLOP per image request for gemma4:31b -- larger than the tower projections or the LM GEMMs -- and appears nowhere in the 11,812 captured calls, because it runs on flash-attention kernels. rocwmma has zero hits in the shipped backend, FA is enabled in production and in the harness, and the headers are in the build base. The biggest block of work is on an unmeasured path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Results in — three questions closed, one bigger one openedPushed in 1. Which caller — settled, and the retraction partly un-retractsDiffing the rocBLAS inventory between the MMQ and
Zero MMQ-build shapes are missing under 2. FP32 accumulate costs 2× and buys nothingFull suite under 3.
|
| model | gate off | gate on | Δ |
|---|---|---|---|
gemma4:31b |
154.2 | 107.7 | −30.2% |
qwen3.8:27b |
243.4 | 162.3 | −33.3% |
Gate-off reproduced the production baseline (154.2 vs 154.9), so the build is production plus the gate and the numbers hold. Cheaper than full FP32 but nowhere near free — and since more precision bought no accuracy in (2), there's no reason to pay for it. Scores under the gate are running.
The largest block of work isn't in this document at all
There is no 9900×9900 shape in any of the 11,812 captured GEMM calls. Tower attention never touches rocBLAS — it runs on flash-attention kernels. For gemma4:31b:
| block | TFLOP / image request |
|---|---|
| tower attention (QK^T + AV) | 12.2 |
| tower projections | 10.8 |
| LM GEMMs | 8.7 |
The biggest single block of vision prefill is one that none of the findings in this PR apply to. Three facts make it the lead:
- flash attention is enabled in production and in the bench harness — those kernels really are running this
rocwmmahas zero hits in the shippedlibggml-hip.so;GGML_HIP_ROCWMMA_FATTNis off (upstream default)- rocWMMA headers and cmake config are in the production build base, so the alternative is buildable
Queued: the rocWMMA build, and a diagnostic run with FA off — which forces tower attention through rocBLAS as explicit mul_mat and is the only way to get 9900×9900 into the inventory. I expect that one to be slower (materialising the matrix is what FA exists to avoid, ~339 GB of traffic over 27 blocks), but the ratio calibrates how much upside rocWMMA can possibly have before spending a build on it.
…ceiling bench The measurement that decides it: rocBLAS reaches 82-87% of theoretical peak on the vision tower shapes. Every other lever tested says "this configuration is not better"; that one says there is little left to be better than. gfx1151 vision prefill is bandwidth-bound, not configuration-bound. ADR 0041 records the decision to stop pursuing ROCm/HIP kernel configuration for this hardware, with the eight levers and their results, so it is not reopened on the same reasoning. It explicitly does not close model-shape work, a future ROCm shipping gfx1151 hipBLASLt kernels, or upstream's MMA FA kernel. gemm_ceiling_bench.cpp is committed as the instrument, because no comparative A/B can answer "is this near the metal" -- every other measurement in this repo divides one configuration by another. SPEC H23: a knob with a prerequisite needs three arms. Flash attention could not be disabled without also moving the KV cache off q8_0. Three arms measured the cache change at 1.0% and FA at 51%; two arms would have said 52% for FA. SPEC H24: a percentage-of-peak names where the peak came from, and a value over 100% means the denominator is wrong. hipGetDeviceProperties returns WGPs on RDNA, not CUs -- 20 against a true 40 -- and the bench printed "165% of peak" until rocminfo settled it. The impossible number is the only reason it was caught. kv-cache-type-per-model.md already documented the FA/V-cache coupling at the b9888 pin; what it lacked was the price, so it gains the measured cost rather than a restatement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hing here The AMD WMMA branch in ggml_cuda_get_best_fattn_kernel excludes head dims 40 and 72 outright and caps at 256, so both gemma4 models run entirely on the generic TILE kernel -- the tower because 1152/16 = 72 is on the exclusion list, the LM because 512 exceeds the cap. That looks like our most-used vision model missing the fast path, so it was worth measuring rather than assuming. compat 805 gates the override so one build A/Bs at runtime. Measured: nemotron3:33b MMA 377.2 -> TILE 372.6 -1.2% qwen3.6:35b-a3b MMA 464.6 -> TILE 458.2 -1.4% gemma4:31b 154.2 -> 151.5 -1.8% <- CONTROL gemma4:31b is already TILE everywhere, so the gate cannot touch it; its -1.8% is run-to-run drift between arms, and both treatment effects are smaller than that. MMA vs TILE is not resolvable at this magnitude. Without the control this would have been reported as "MMA is worth 1.2-1.4%". The gate is verified to fire, not merely to compile: 0 log lines unset, 2 when set. The measured deltas are smaller than drift, so "gate broken" and "gate works and changes nothing" would otherwise be indistinguishable -- the same gap that compat 802 had to close between compiling and emitting. This also corrects the 51% figure: that is what the TILE kernel is worth on gemma4:31b, not the MMA kernel. ADR 0041 gains speculative decoding in its "does not close" list. The ADR was written about prefill and that was a gap: dense decode here runs at 75-85% of memory bandwidth, and spec decode is the one technique that beats that bound by amortising the weight read across several tokens. b10969 supports --spec-type draft-dflash and ollama passes it through. Unmeasured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The earlier commit added the patch without its Files entry. Two causes, both mine: the anchor pattern did not match, and the script had no `set -e` so the commit ran anyway. The branch was also cut from origin/main before #370 landed, so there was no 802 entry to anchor to until main was merged in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… compat 805 for b11081 Main moved from b10969 to b11081 (the v0.34.4 fold) since this branch last merged it. - SPEC: main reserved H23 and H24 for this PR and added H25 after H22. The conflict was placement only: H23 and H24 now sit between H22 and H25, and the conformance table gains their rows. H23's example now says production ran q8_0 in 2026-09 (it runs f16 since ADR 0043). - compat 805 no longer applied: b11081's fattn.cu changed the context above flash_attn_mask_to_sparse_indices. Regenerated against the pinned source; its added lines are unchanged. The AMD WMMA branch it explains still excludes head dims 40 and 72 and caps at 256 at b11081. Verified: CI's patch check (cmake configure of llama/server) applies every compat patch, and the patched fattn.cu builds as a HIP object for gfx1151 (Dockerfile.rocm's toolchain stage, ROCm 7.2.4). - kv-cache-type-per-model.md: the q8_0 -> f16 cost note says production runs f16 since 2026-09-26 (ADR 0043), so only the flash-attention cost remains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Brought up to date with main (b11081) in 93e2f2d. It had conflicted since the v0.34.4 fold.
|
Closes out the question I raised at the end of the MMQ vs hipBLAS A/B: could rocBLAS offer better kernels on gfx1151? It was worth asking. The answer is no, and the reasons are more useful than the answer.
All measured on gfx1151, build
0.34.2-dynres-f67b1aef, ROCm 7.2.4. Noise floor ±0.4%.1. These GEMMs never reach
should_use_mmqggml_cuda_mul_matbails to cuBLAS at its first branch:Every captured call has
--d_type f16_r, so every one takes this exit.This corrects how the earlier A/B should be read.
GGML_CUDA_FORCE_CUBLASonly changes whatshould_use_mmqreturns, so it cannot move any of these GEMMs — they were on rocBLAS in both arms. That is why densegemma4:31bcame out at +2.1% instead of showing an MMQ win: a large share of its prefill never had a choice of path. The A/B is still valid for what it measured; it simply never covered this.2. It is not a vision-tower story
I had been calling this "the tower". Measured, it isn't:
qwen3.8:27bgemma4:31bgemma4:26b-a4bnemotron3:33bqwen3.6:35b-a3bAttribution uses each model's own GGUF dimensions.
qwen3.8reports no vision dims at all, so its bf16 tower work is reported as "other" rather than assigned to a tower whose size the model never states — an early draft guessed 1152/4304 by analogy with gemma4 and would have published that guess as fact.qwen3.6sending the least work here is consistent with it losing the most (−35.6%) when MMQ was disabled: MMQ does nearly all of its matmul work.3. Both runtime routes are closed
Three cold repeats on
gemma4:31b, one per distinct image, first request after start discarded:ROCBLAS_USE_HIPBLASLT=1GGML_CUDA_CUBLAS_COMPUTE_TYPE=f32hipBLASLt is inert, and for a packaging reason. Upstream calls it better than Tensile on CDNA3. Here it does nothing — because neither the ROCm 7.2.4 image nor the ROCm 10 image ships a single gfx1151 hipBLASLt kernel file, despite the
.soreferencingTensileLibrary_*_gfx1151. Revisiting means shipping kernels, not flipping a flag.Compute type is already the fast one by 2×. Every call runs
compute_type f16_r. Forcing FP32 accumulate halves prefill, tightly (79.6 / 79.7 / 79.6) and in line with what RDNA3.5 WMMA predicts. There is no headroom to recover.What this rules out
default: return truethreshold — the A/B measured that path at −0.1% on denseqwen3.8:27b.Still open
dst->type != F32. An F16-output path throughmmfwould avoid it for the tower shapes — an upstream design question, and the one place real headroom might be.Contents
docs/maxusai/rocblas-on-gfx1151.mdvision-suite/gemm_shapes_gfx1151.tsvvision-suite/summarize_gemm_shapes.pyvision-suite/shape_inventory.shThe −48.6% doubles as a regression sentinel: if prefill on this host ever halves, check
compute_typebefore anything else.🤖 Generated with Claude Code