Repository navigation
ggml-hrx: route Q4_K/Q5_K/IQ4_XS prompt matmuls to the q8_1 x4 kernel (pp512 99 -> 341 on Qwen3.8-27B) - #55
Merged
bong-water-water-bong merged 2 commits intoOct 1, 2026
Conversation
The q8_1 x4 prefill matcher accepted Q5_K and IQ4_XS weights only when an earlier dispatch had already produced q8_1 activations, and never when the matmul feeds a GLU; Q4_K was allowed both. On Qwen3.8-27B UD-Q4_K_XL, whose projections are mostly Q5_K and IQ4_XS, that left 94% of prompt time in the generic mul_mat_f32_f32_wmma (an iree-profile dispatch trace of a 1.8K-token prompt: mul_mat_swiglu 51%, mul_mat_add 25%, mul_mat 18%). Q5_K and IQ4_XS now follow the Q4_K policy (standalone activation quantize, GLU consumers allowed). GGML_HRX_Q8_PREFILL_RELAX=0 restores the old policy. gfx1151, llama-bench -b 512 -ub 512, old -> new: pp512 99.1 -> 143.9, pp2048 99.3 -> 138.6 tok/s. KLD against the BF16 logits (wikitext-2, 20 x 512): 0.00717 -> 0.00720, same top token 96.13% -> 96.24%. test-backend-ops MUL_MAT on HRX0: 265/265. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… kernel With Q5_K/IQ4_XS on the q8_1 x4 prefill kernel, the FFN gate/up projections still went to the generic f32 WMMA mul_mat_swiglu: it is registered at priority 290, above the q8_1 x4 matcher (285), and claims gate, up and the GLU together. It was 59% of prompt time on Qwen3.8-27B UD-Q4_K_XL. For prompt chunks of 256-2048 tokens (multiple of 256) whose gate and up weights are Q4_K, Q5_K or IQ4_XS, the generic fused matcher now declines, so each projection takes the q8_1 x4 kernel and the GLU runs as its own op. It follows the same switch: GGML_HRX_Q8_PREFILL_RELAX=0 restores the old routing. The policy helper moves to dispatch-mul-mat-common.h as common_q8_prefill_relaxed(). gfx1151, llama-bench -b 512 -ub 512, GGML_HRX_Q8_PREFILL_RELAX=0 -> default (both commits of this branch): pp512 99.7 -> 340.7, pp2048 99.0 -> 317.7 tok/s. KLD against the BF16 logits (wikitext-2, 20 x 512): 0.00717 -> 0.00712, same top token 96.13% -> 96.27%. test-backend-ops MUL_MAT on HRX0: 265/265. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong
left a comment
Author
There was a problem hiding this comment.
Review (PR-Agent duty). A large showcase win (Qwen3.8-27B HRX pp512 99.7 -> 340.7, pp2048 99.0 -> 317.7) with KLD vs BF16 unchanged (0.00717 -> 0.00712) and MUL_MAT ops 265/265. The approach is right.
Checked:
- Rule 1 (Q5_K / IQ4_XS take the Q4_K q8_1 x4 policy): standalone q8_1 quantize, GLU consumers allowed. It's gated by
common_q8_prefill_relaxed(), read once, default on, with=0restoring the old routing. - Rule 2 (
mul_mat_swigludeclines for 256..2048-token chunks in multiples of 256, when both gate and up are Q4_K/Q5_K/IQ4_XS): each projection then takes q8_1 x4 and the GLU runs as its own op. If the q8_1 x4 matcher declines some shape, the node falls back to the generic per-op path, which is correct, just slower. So the decline can't strand a node. - Decode untouched: both rules need at least 256 tokens.
Before merge:
- The two Windows
Testfailures (x64-openblas, x64-vulkan). HRX isn't built on Windows, so this change likely isn't the cause, but #54's Windows jobs passed. Please re-run them, or show the failing test is pre-existing. Their logs weren't available when I looked. - The numbers in the code comments (
pp512 143 -> 363,99 -> 141, KLD 0.00720) are from the intermediate builds and differ from the PR table. Fine as history, but say which step each refers to, or use the final numbers, so nobody reads 363 as current.
Non-blocking: this is policy logic in AMD's files (dispatch-mul-mat.cpp, dispatch-gated-mul-mat.cpp, dispatch-mul-mat-common.h), about 35 lines. It's acceptable as small hooks, but if it grows, move common_q8_prefill_relaxed() and the format predicate into a headed file of ours.
Mergeable once (1) is cleared.
bong-water-water-bong
merged commit Oct 1, 2026
6e42b51
into
1bit/hrx-vulkan-patched
8 of 24 checks passed
bong-water-water-bong
added a commit
that referenced
this pull request
Oct 2, 2026
…_q4_0) through llama-hadamard (#58) The engine's tools/hadamard_q4_0.py writes Q4_0 files whose matmul weights are rotated per 32-element block along the input dimension by the normalized Sylvester Walsh-Hadamard matrix, without signs, and stamps onebit.hadamard_q4_0 = 32. That is the prism.hadamard transform with block_size 32 and sign_mode identity, so llama-hadamard now reads the stamp too: the rotated weights are the file's Q4_0 tensors on a verified matmul path (a Q4_0 lookup table such as token_embd is written in the plain basis). The activation rotation is an ordinary MUL_MAT against H, so the files run on HRX and every other backend, where before only the engine's ROCm build (which rotates inside its quantizers) could run them. - ssm_alpha / ssm_beta join the verified kinds: qwen35 and qwen35moe build both through build_lora_mm, and the engine's files rotate them. - A stamped weight the model does not load (the MTP layer, blk.64, without --mtp) is skipped; prism.hadamard's named weights stay strict. - verify_graph still refuses a folded weight that reaches a matmul without its transform. Qwen3.8-27B-Q4_0-H32 on HRX0, llama-perplexity, wikitext-2 40 x 512 against the BF16 logits: PPL 6.021, KLD 0.0292, same top token 92.5% - the ROCm build's exact int8 path gives 6.008 / 0.0284 / 92.5% (without the rotation HRX gave PPL 550,134). llama-bench on HRX0 (before fork #55): pp512 63.5, pp2048 62.4, tg64 11.65 tok/s. Q4_0 prompt matmuls still take HRX's generic path; #55 routes the K-quants and IQ4_XS only. Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: Claude Opus 5.5 <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.
On Qwen3.8-27B UD-Q4_K_XL, 94% of HRX prompt time went to the generic f32 WMMA matmul kernels, which work in 32-token tiles. An iree-profile dispatch trace of a 1.8K-token prompt:
mul_mat_swiglumul_mat_addmul_matThis model's projections are mostly Q5_K and IQ4_XS. Two routing rules kept them away from the q8_1 x4 prefill kernel:
89078ac: the q8_1 x4 matcher took Q5_K and IQ4_XS only when q8_1 activations already existed, and never for a matmul feeding a GLU. Q4_K was allowed both. Q5_K and IQ4_XS now get the Q4_K policy.5085ae2: even then, the FFN gate/up projections went to the generic fusedmul_mat_swiglu. It is registered at priority 290, above the q8_1 x4 matcher (285), and claims gate, up and the GLU together; that was 59% of prompt time. For prompt chunks of 256-2048 tokens in multiples of 256, whose gate and up are Q4_K, Q5_K or IQ4_XS, it now declines. Each projection then takes the q8_1 x4 kernel and the GLU runs as its own op.GGML_HRX_Q8_PREFILL_RELAX=0restores the old routing for both.test-backend-ops -o MUL_MAT -b HRX0: 265/265. Decode is untouched, since both rules apply only to chunks of at least 256 tokens.🤖 Generated with Claude Code