Summary
Implement the Q8_0 outer-product GEMM kernel using AVX2 VNNI (AvxVnni.MultiplyWideningAndAdd → VPDPBUSD-256), completing the Q8_0 half of ROADMAP Phase 3 Step 26 (the F32 half is #312 / PR #315).
Background
PR #61 landed the Q8_0 outer-product family but left it register-pressure-bound: the AVX2 microkernel holds only 3 live accumulators (one weight row at a time) to stay under the 16-YMM ceiling, because the s8×s8 reduction needs a maddubs + madd(ones) pair plus a ones mask and a prod temporary.
AVX2 VNNI (AvxVnni, shipped since .NET 5) fuses multiply + widen + accumulate into a single VPDPBUSD, eliminating the ones register and the prod temporary. That lifts safe accumulator residency from 3 → 6 rows and clears the ~23-YMM problem that blocked #61's dispatch.
Scope
OuterProductQ8_0Vnni_4x3 in src/DotLLM.Cpu/Kernels/MatMul.cs: acc = AvxVnni.MultiplyWideningAndAdd(Vector256<int>.Zero, absX.AsByte(), adjW), replacing the AVX2 integer-reduction pair. Same 4-row × 3-token output tile, abs/sign idiom and per-block dw·dx float fold preserved → bit-identical per cell to the AVX2 path (same FMA order).
- Dispatch: VNNI branch ahead of AVX2 in
OuterProductGemmQ8_0 (single-threaded) + OuterProductGemmQ8_0Worker (parallel); not taken on AVX512BW CPUs (they keep the 4×6 AVX512 path) — targets AVX2+VNNI-without-AVX512.
- Register budget: 4-row R4 group processed as two 2-row sub-passes → 6 float acc + 3 token vectors + ~4-5 transients ≈ 13-14 YMM ≤ 16.
Acceptance criteria
References
Summary
Implement the Q8_0 outer-product GEMM kernel using AVX2 VNNI (
AvxVnni.MultiplyWideningAndAdd→VPDPBUSD-256), completing the Q8_0 half of ROADMAP Phase 3 Step 26 (the F32 half is #312 / PR #315).Background
PR #61 landed the Q8_0 outer-product family but left it register-pressure-bound: the AVX2 microkernel holds only 3 live accumulators (one weight row at a time) to stay under the 16-YMM ceiling, because the s8×s8 reduction needs a
maddubs+madd(ones)pair plus aonesmask and aprodtemporary.AVX2 VNNI (
AvxVnni, shipped since .NET 5) fuses multiply + widen + accumulate into a singleVPDPBUSD, eliminating theonesregister and theprodtemporary. That lifts safe accumulator residency from 3 → 6 rows and clears the ~23-YMM problem that blocked #61's dispatch.Scope
OuterProductQ8_0Vnni_4x3insrc/DotLLM.Cpu/Kernels/MatMul.cs:acc = AvxVnni.MultiplyWideningAndAdd(Vector256<int>.Zero, absX.AsByte(), adjW), replacing the AVX2 integer-reduction pair. Same 4-row × 3-token output tile, abs/sign idiom and per-blockdw·dxfloat fold preserved → bit-identical per cell to the AVX2 path (same FMA order).OuterProductGemmQ8_0(single-threaded) +OuterProductGemmQ8_0Worker(parallel); not taken on AVX512BW CPUs (they keep the 4×6 AVX512 path) — targets AVX2+VNNI-without-AVX512.Acceptance criteria
GemmQ8_0— deferred. The Q8_0 outer-product family requires R4-repacked weights and currently has no production callers; the publicGemmQ8_0(n>1) path routes row-major weights throughComputeGemmTiled. Wiring needs a per-call full-matrix R4 repack (architecturally significant) — tracked separately, mirrors the deferred F32 caller-wiring in kernels(cpu)(matmul): F32 outer-product tiled GEMM kernel for prefill (ROADMAP Phase 3 Step 26) #312/kernels(cpu)(matmul): F32 outer-product tiled GEMM kernel for prefill #315.References
ggml-cpuVNNI int8 path (mul_sum_i8_pairs) — authoritative semantics