Repository navigation
cuda: PQ2_0 mat-vec kernel for 3-8 columns on Ada - #306
Conversation
The generic mmvq kernel loses DRAM throughput from 3 columns on (RTX 4070, Bonsai 2 27B shapes: 46% of peak at 4 columns, 36% at 8). Add a dedicated kernel for plain 2D PQ2_0 calls with 3-8 columns on Ada. It uses a new activation layout, GGML_CUDA_Q8_1_PQ2: the qs bytes of a column are permuted inside each 16-element group and the half2 (d, int sum) per 32-block follow. With that layout (code_word >> 2k) & 0x03030303 pairs the raw 2-bit codes with the activation bytes, so dp4a needs no per-weight decode, and the digit bias is one integer subtraction. A warp handles 4 rows and reuses the activation slice; registers are capped at 168 (3 blocks per SM). GGML_CUDA_PQ2_MULTICOL=0 turns it off. Other architectures, 1-2 columns, ids and batched calls keep the generic kernel. Tested on RTX 4070 (sm_89): - test-backend-ops MUL_MAT pq2_0: 219/219 pass - kernel, cold L2 (ncu), m=5120 k=17408: n=4 105.6 -> 54.0 us, n=8 136.8 -> 78.1 us - Ternary-Bonsai-2-27B-PQ2_0, llama-batched-bench decode: 4 seq +17%, 6 seq +25%, 8 seq +29% - perplexity (256 ctx, 6 chunks, -ub 8/5/4): 6.3784/6.3784/6.3776 -> 6.3774/6.3774/6.3820 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The environment switch of the previous commit was only there to measure the change; the multi-column kernel is now selected by the conditions alone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
@sb32445 @synchronic1, #243 contains two PQ2_0 multi-column MMVQ commits (2c9103d, 356b782) that cover the same ground as this PR. Could you compare notes and settle on one kernel? @synchronic1, #243 also bundles a chart, IQ4_XS/Q5_0 changes and a fused q8_0 FA path and conflicts in five files, so if there is a part you want reviewed, a separate focused PR per change would be much easier to land. |
|
@bri-prism Thanks for flagging the overlap. I’ll split PQ2_0 out and coordinate with sb32445 on shared quantization and dispatch. Ampere here, Ada there; arch-specific paths may make sense, subject to tests. @sb32445 Happy to coordinate. I’m preparing Ampere correctness and fallback checks; your Ada kernel won’t execute on my 3060 unchanged. Could we agree on shared quantization and dispatch, with each of us validating on our hardware? |
|
@bri-prism @sb32445 Thanks for flagging the overlap, and thanks for the Ada work. We completed RTX 3060 (Ampere) validation of the isolated PQ2_0 changes: all 350 test calls passed, and PR306 correctly selected its fallback on Ampere. This supports compatibility of the tested path on Ampere; it does not yet validate a combined patch or establish that the changes merge cleanly. @sb32445 Would you be willing to validate the isolated Ampere changes and any proposed shared quantization/dispatch changes on your Ada hardware? We don’t currently have Ada hardware available and can cover the Ampere side. @bri-prism Would you prefer one focused, coordinated PQ2_0 PR retaining the appropriate Ampere and Ada paths, or separate PRs built on shared groundwork? I’ll separate the unrelated changes from PR243 as requested. |
|
@bri-prism @synchronic1 thanks for the quick Ampere check, and for splitting #243. I read both kernels against each other, and I think they do not need to be merged into one, because they never select the same hardware:
So the dispatch is disjoint by architecture: Ampere takes the #243 path, Ada takes #306, everything else (and 2 columns on Ada) stays on the generic kernel. If I read #243 wrongly, please correct me. What follows from that:
If you would rather have one combined PQ2_0 PR with both paths, I am fine with that too, but I think two focused PRs are easier to review and to validate per GPU. |
|
Independent validation of the current PR head on an RTX 4090 (native sm_89), CUDA 12.8.93, driver 580.159.03. Compared the exact parent against the unmodified PR using identical Release builds and the public PQ2_0 model referenced in the PR. The model was downloaded at a pinned revision and its SHA256 verified. Library-loader checks confirmed the intended parent/head libraries. Batch-invariant mode was unset. Whole-model decode: three alternating AB / BA / AB comparisons with
Unchanged-dispatch controls at sequences 1/2/9 had median changes of -0.08%/+0.20%/-0.01%. However, individual runs varied substantially, including in those controls: raw paired gains were +0.85/-6.40/+9.51% at sequences 4, +6.24/-4.25/+17.92% at 6, and +11.39/+0.15/+20.68% at 8. Batch 8 is the strongest result; batch 4's small gain is unresolved, and batch 6 was not faster in every pair. This does not reproduce a blanket 17–29% gain on this different Ada GPU. Operation timings: with a 192MiB cache sweep outside the CUDA event interval, K17408/M5120 improved 52.13→44.03µs at columns 4 and 59.39→45.79µs at 8. K5120/M17408 improved 53.25→45.06µs at 8, but regressed 43.26→45.06µs at 4. These results were repeatable across rounds. Each round used 101 samples after 20 warmups. The timer covers backend graph execution, activation quantization and scheduling gaps; it is not an isolated kernel timer or a reproduction of the reported NCU measurements. Correctness: parent and head each passed 302/302 PQ2 Optional Nsight Compute profiling was denied GPU-counter access, so those attempts are excluded from the timings. No runtime kernel-name trace, generated-text quality, perplexity, long-context or speculative-acceptance result is claimed. Overall: correctness checks pass and there is a useful, shape-dependent Ada performance benefit, strongest at eight sequences on this card. Please preserve the batch 4 operation regression and whole-model timing variability when describing expected gains. |
PrismML-Eng#313 merged, five open pull requests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Overview
Adds a mat-vec kernel for PQ2_0 weights with 3-8 activation columns on Ada (cc 8.9). The generic mmvq kernel drops to 36-66 % of DRAM bandwidth from 4 columns on. The new kernel feeds the raw 2-bit codes straight into
dp4awith an integer correction term (new activation layoutGGML_CUDA_Q8_1_PQ2), 4 rows per warp,__launch_bounds__(128, 3).RTX 4070, m=5120 k=17408 (cold, ncu): 4 columns 105.6 -> 54.0 us, 8 columns 136.8 -> 78.1 us.
Ternary-Bonsai-2-27B-PQ2_0, parallel decode sequences: 4: +17 %, 6: +25 %, 8: +29 %.
Additional information
GGML_CUDA_PQ2_MULTICOL(=0switches the kernel off) that was used for the measurements below, the last one removes it. To reproduce a measurement, build the first commit.cc == GGML_CUDA_CC_ADA_LOVELACEexactly (8.9), the registers (168, 3 blocks per SM) are tuned for that GPU.ggml_cuda_q8_1_layout_hostgot aplain_2dargument; the layout choice for the quantizer and the kernel choice come from the samey_layout, and the PQ2 branch asserts that no ids, batch dims or fusion are involved (fusion with more than one column does not exist for PQ2_0).Test results
-DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=89.prismat88c4bc60b; the four commits since (SYCL, WebGPU andcuda: fused FWHT quantizer for 64-wide warps (#303)) touchquantize.cuonly in the 64-wide-warp FWHT path, not the code of this PR. The branch is rebased on2459f68b5, builds, andtest-backend-opswas repeated on it.test-backend-ops test -b CUDA0 -o MUL_MAT: 1586/1586 passed on the rebased branch (CUDA0 against CPU; includes the PQ2_0 cases with odd row counts). On the earlier base the PQ2_0 subset alone was 219/219.llama-batched-bench): 4 sequences +17 %, 6 sequences +25 %, 8 sequences +29 %.-ub 8 / 5 / 4) before -> after: 6.3784 / 6.3784 / 6.3776 -> 6.3774 / 6.3774 / 6.3820, a difference of at most 0.07 %, far inside the reported error of +-0.53.-np 4, 4 different prompts sent at once, 192 tokens, q4_0 K/V, two rounds per setting): with the new kernel both rounds are identical for all 4 prompts. With the generic kernel (GGML_CUDA_PQ2_MULTICOL=0) two identical runs already differ for 3 of 4 prompts (the arithmetic of the generic kernel depends on the number of columns, and the batch composition varies slightly between runs). Old against new: 2 of 4 prompts identical, the others first differ at character 161 of 848 and 407 of 838.ids) models, column counts outside 3 to 8 (they take the old path), a task-level quality metric.Requirements