ggml-cpu: add 2x2 register-blocked AVX2 kernel for ggml_vec_dot_q8_0_q8_0 - #45
Open
codspeed-hq[bot] wants to merge 1 commit into
Conversation
…q8_0 ggml_compute_forward_mul_mat() calls the dot product once per (src0 row, src1 column) pair, so every Q8_0 weight block is reloaded and every FP16 scale reconverted once per column of the tile. Add an nrc == 2 path to the AVX2 ggml_vec_dot_q8_0_q8_0() that computes a 2x2 output tile (two src0 rows x two src1 columns) in one pass, and enable nrows = 2 for GGML_TYPE_Q8_0 on AVX2. Per 32-value block the tile issues 4 loads instead of 8, 4 scale conversions instead of 8 and 6 vpsignb instead of 16, with all four accumulators kept in registers. Each of the four sums uses its own accumulator, visits the blocks in the same order and applies the same d_x*d_y product as the 1x1 loop, so the results are bit-identical.
Author
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.
What
ggml_compute_forward_mul_mat()calls the dot product once per (src0 row, src1 column) pair, so a Q8_0 matmul over an F32 activation re-loads every weight block - and re-converts every FP16 scale - once per column of the tile.This adds an
nrc == 2path to the AVX2ggml_vec_dot_q8_0_q8_0()that computes a 2x2 output tile (two src0 rows x two src1 columns) in one pass, and enablesnrows = 2forGGML_TYPE_Q8_0on AVX2 (the ARM i8mm path already usednrows = 2; Q4_0/Q4_1/Q4_K/Q6_K already declare it too).Why it wins
Very little of the per-block work is the multiply-accumulate itself. Per 32-value block a 1x1 dot product pays 2 loads, 2 FP16 scale conversions and 2
vpsignb(_mm256_maddubs_epi16()needs an unsigned first operand, so|x|andsign(x)*ymust be materialized). In the tile,|x0|/|x1|and all four operands are shared across the four accumulators:vpsignbThe four
ymmaccumulators and all operands stay in registers (no stack traffic).Measured effect
CodSpeed CPU simulation, full macro suite (
benchmarks/bench_macro.cpp), measured locally on the same machine for base and head:prompt_layer[q8_0]lm_head[q8_0]The other 11 macro benchmarks are unchanged.
decode_layer[q8_0]generates a single token (ne11 == 1), so it keeps the 1x1 path.For
prompt_layer[q8_0]the instruction component drops (82.7% -> 81.5% of a 28% smaller total) and the cache component shrinks as each weight block is now read once for two columns.Correctness
Results are bit-identical: each of the four sums uses its own accumulator, visits the blocks in the same order and applies the same
d_x*d_yproduct as the 1x1 loop.ggml_mul_matoutput match an unmodified build over 105 Q8_0 cases (K in {32, 64, 256, 2048, 5632}, N in {33, 128, 2048}, M in {1, 2, 3, 8, 16, 17, 64}, 2-D and batched 3-D, with 1/2/8 threads); the 105 Q4_0 control cases are unchanged as well.nr0 % 2 == 0 && ne11 % 2 == 0conditions inggml_compute_forward_mul_mat), and none of the Q4_0 hashes.ctest -L mainpasses (46/47; the only failure,test-tokenizers-ggml-vocabs, is a pre-existing environment issue - the git-lfs vocab files are not fetched in the sandbox).Scope
Only the AVX2 path in
ggml/src/ggml-cpu/arch/x86/quants.cplus thenrowsflag are touched, so this is an x86 change; the aarch64 kernel is untouched and unaffected. The tile only applies when the number of src1 columns is even, i.e. prompt processing and the LM head - single-token decode keeps the existing kernel.