ggml-cpu: add x86 AVX-VNNI dot product for PTQ1_0 - #181
AlexGabbia wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new explanatory comment violates the repository's concise, non-hard-wrapped comment convention.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds an x86 VNNI-accelerated PTQ1_0/Q8_0 dot product while preserving the generic fallback.
Changes:
- Decodes packed ternary values with AVX2/VNNI intrinsics.
- Removes the obsolete x86 generic alias.
File summaries
| File | Description |
|---|---|
ggml/src/ggml-cpu/arch/x86/quants.c |
Adds the optimized dot-product kernel. |
ggml/src/ggml-cpu/arch-fallback.h |
Enables x86-specific dispatch. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
PTQ1_0 had only the generic scalar vec_dot on x86. Add an AVX2 +
AVX-VNNI / AVX-512-VNNI implementation of ggml_vec_dot_ptq1_0_q8_0,
following the same shape as the existing PQ2_0 kernel:
- decode the base-3 packed trits ((b * 3^p) & 0xFF) * 3 >> 8 to {0,1,2}
codes with 16-bit lane arithmetic, no lookup tables
- dot(code - 1, qy) = dpbusd(code, qy) - dpbusd(ones, qy)
- per 32-wide q8_0 sub-block the integer sum and float accumulation
order match the generic kernel exactly
test-quantize-fns passes. Ternary-Bonsai-2-27B PTQ1_0, CPU only, 16
threads, Core Ultra 9 275HX (AVX-VNNI, no AVX-512):
generic this kernel
MSVC 19.44 0.79 tg64 / 0.85 pp256 2.87 tg64 / 3.78 pp256 (3.6x / 4.4x)
clang 22 4.58 tg128 / 6.03 pp512 4.65 tg128 / 6.10 pp512 (parity)
MSVC does not auto-vectorize the generic decode, so the explicit
kernel gives shipped Windows binaries a 3.6-4.4x CPU speedup. Under
clang the generic auto-vectorizes to near-parity; the kernel pins the
fast path independently of the compiler.
2573dd2 to
104bf3b
Compare
|
Thanks - the comment block is now two lines and keeps only the non-obvious VNNI transformation. The element order and the packing details are readable from the code below it and from dequantize_row_ptq1_0, so they do not need restating. Verified again after the edit: test-quantize-fns passes (clang 22, AVX-VNNI build), no functional change. |
bri-prism
left a comment
There was a problem hiding this comment.
Agent review: posted by the maintainer's coding agent at their request.
No findings in this source pass. The SIMD packing/sub-block mapping and generic fallback were inspected; the unsigned-code dot minus activation-sum correction preserves the intended integer dot.
Native x86/VNNI compilation and test-quantize-fns were not run here. The compiler-specific results in the PR description are contributor evidence, not an independent reproduction by this review.
Reviewed commit: 104bf3bafb1b2f89a108b8b5a7e8c8d0f33fc87a.
|
Adding the independent validation that the review notes was missing ("Native x86/VNNI compilation and Environment: Core Ultra 9 275HX (Arrow Lake-HX, AVX-VNNI, no AVX-512), Windows 11, MSVC 19.44 (cl.exe 14.44.35207), built with
Effect on the shipped Windows toolchain, The Copilot note about the comment style is cosmetic; I can shorten that comment if you prefer. |
|
Merge-order note, in case it is useful: #248 adds an x86 |
|
Tested on an Intel laptop. This CPU takes the AVX-VNNI path. Setup: Intel Core Ultra X7 358H (Panther Lake, 16 cores, hybrid; AVX2 + AVX-VNNI, no AVX-512), Windows 11, MSYS2 UCRT64 GCC 16.2, Correctness: matches the generic kernel, as claimed
Performance. Unlike the clang 22 parity result in the description, GCC 16 does not auto-vectorize the generic decode enough either, so the gain isn't MSVC-only:
(2B pp64 is noisy in both arms.) Overlap: #250 and #248 rewrite the same function, so the three conflict. On this box, 27B PTQ1_0 pp64 / tg32 is #250 4.65 / 2.00, #248 (SSSE3, on its older base) 3.87 / 1.83, this PR 2.70 / 1.47. #250 is faster by about 1.7x prefill and 1.35x decode, but gives up exact order-matching (its KLD is still negligible: max 0.0022, 99.2 % same top). Worth deciding between them. Not tested yet: an MSVC build (Build Tools 2022 is on the box; I can follow up with MSVC numbers). Tested with Claude Code. |
|
Follow-up with MSVC numbers, as promised. Your MSVC claim reproduces. Setup: Core Ultra X7 358H (AVX2 + AVX-VNNI, no AVX-512), Windows 11. MSVC 19.44.35228 (VS 2022 Build Tools),
That's in line with your 3.6x / 4.4x. MSVC's generic path is 2–2.6x slower than GCC's here, which confirms it doesn't vectorize the decode loop. For the choice between the overlapping PRs, same session and flags: #250 under MSVC is 2.97 (27B tg16), 31.6 (2B tg32) and 53.2 (2B pp64). That's about 2.0–2.4x this PR under MSVC. This PR's advantage remains exact agreement with the generic kernel's output. Tested with Claude Code. |
|
Kernel-level note on the three overlapping PRs, on one machine with all three kernels in the same binary. I had posted merge-order advice on #181 earlier (tiers: #250 AVX2 / #181 VNNI / #248 SSE2). That was wrong, and the measurement says why. Setup: Core Ultra 9 275HX (Arrow Lake-HX, AVX2 + AVX-VNNI, no AVX-512), Windows 11, MSVC 19.44.35207, Release, single thread. Each binary links one PR's
The advantage is stable across three cache regimes and two sessions. Disassembly confirms each kernel is the real one (8 Two things I did not know when I posted the tier plan: 1. #250 already takes the VNNI path. 2. The bit-exactness claim is narrower than it looks. Comparing kernel against the generic one on 17500 real quantized inputs (nb = 1, 2, 4, 8, 20, 40, 80):
#250 pre-multiplies But #248 has the same property and is 1.4× faster than #181, while also covering pre-Haswell x86-64 and PQ2_0. So #181 is dominated on both axes: #248 beats it on speed at equal exactness, #250 beats it on speed at the same CPU coverage. Worth adding for the record: #250 also passes #248's new I am not asking anyone to keep this open on my account. On these numbers #248 + #250 cover the space and #181 adds no coverage: #248 for the SSE2 baseline and exactness, #250 for AVX2/AVX-512 speed. Closing #181 in favour of them is the right call, and the kernel is on record here and in #248's review thread if the exactness property is ever wanted back. One caveat worth stating rather than hiding: this is single-threaded, and in the CPU decode path of Bonsai 2 27B token generation is memory-bound. The ranking is what I measured; the end-to-end effect on tg should be checked by whoever owns the CPU path. Independent measurement, no affiliation with any of the three PRs. |
|
Thanks for the careful measurement and for recommending this yourself. Agreed: with #248 merged and #250 covering AVX2/AVX-VNNI at about 3x this kernel's speed, #181 adds no coverage, so we're closing it in favour of those two. Your exactness analysis is a useful record if bit-reproducibility against the generic path is ever needed. Thank you. |

ggml-cpu: add x86 AVX-VNNI dot product for PTQ1_0
PTQ1_0 currently has only the generic scalar
vec_doton x86 (arch-fallback.haliases it with a "until a SIMD version lands" note). This adds an AVX2 + AVX-VNNI / AVX-512-VNNI implementation ofggml_vec_dot_ptq1_0_q8_0, following the same shape as the existing PQ2_0 kernel:((b * 3^p) & 0xFF) * 3 >> 8to{0,1,2}codes with 16-bit lane arithmetic, no lookup tablesdot(code - 1, qy) = dpbusd(code, qy) - dpbusd(ones, qy)Validation
test-quantize-fnspasses on clang and MSVC builds.Ternary-Bonsai-2-27B-PTQ1_0.gguf(26.9B), CPU only, 16 threads, Core Ultra 9 275HX (Arrow Lake-HX, AVX-VNNI, no AVX-512), llama-bench:MSVC does not auto-vectorize the generic decode loop, so the explicit kernel is a 3.6-4.4x decode/prefill speedup for the Windows release binaries. Under clang the generic auto-vectorizes to near-parity, so the kernel mainly pins the fast path independently of the compiler.
Notes
#if defined(__AVX512VNNI__) && defined(__AVX512VL__) || defined(__AVXVNNI__)builds take the new path; other x86 builds fall back to the generic implementation exactly as before.arch-fallback.halias for x86 is removed; the ARM andGGML_CPU_GENERICaliases stay.