ggml-cpu: add 2x2 register-blocked AVX2 kernel for ggml_vec_dot_q8_0_q8_0 - #47
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 with nrows = 1 every Q8_0 block of a 2x2 output tile is loaded twice, every block scale is converted from FP16 twice, and the absolute value of each src0 block is computed twice. Add an nrc == 2 path to the AVX2 ggml_vec_dot_q8_0_q8_0() that computes the whole 2x2 tile in a single pass over the blocks, and enable nrows = 2 for GGML_TYPE_Q8_0 under __AVX2__ (it was already 2 for ARM i8mm, whose kernel has its own nrc == 2 path). Per tile this is 4 block loads, 4 scale conversions and 2 abs ops instead of 8, 8 and 4. The nrc == 1 path, the AVX/SSE paths and the scalar fallback are untouched, and the mat-mul driver already falls back to nrc = 1 when the tile does not fit the shapes. Each accumulator sums the same per-block products in the same order as the 1x1 path, so results are bit-identical: verified against 4 independent 1x1 dot products for n = 32/64/256/2048/5632, and the full Q8_0 mul_mat output (4 threads, incl. odd shapes 256x63x7, 512x66x6, 5632x130x3) is byte-for-byte equal to a build without this change.
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 vec_dot kernel once per (src0 row, src1 column) pair. For Q8_0 weights on x86 the kernel was always invoked withnrc == 1, so a 2x2 output tile costs four independent dot products: every Q8_0 block of both operands is loaded twice, every block scale is converted from FP16 twice, and the absolute value of each src0 block (needed to feed the unsigned side of_mm256_maddubs_epi16) is computed twice.The
prompt_layer[q8_0]profile shows exactly this:ggml_vec_dot_q8_0_q8_0dominates the benchmark, and inside it the block loads and the FP16 scale conversions are the largest single items after the dot product itself.How
nrc == 2path to the AVX2ggml_vec_dot_q8_0_q8_0that computes the whole 2x2 tile (two src0 rows x two src1 columns) in a single pass over the blocks: 4 block loads, 4 scale conversions and 2vpsignb(abs) ops per tile instead of 8, 8 and 4, with all four accumulators kept in registers..nrows = 2forGGML_TYPE_Q8_0under__AVX2__(it was already 2 for ARMi8mm, whose kernel has its ownnrc == 2path), so the mat-mul driver requests the tile.nrc == 1path, the AVX/SSE paths and the scalar fallback are untouched. The driver already falls back tonrc = 1when the tile does not fit the shapes, andmul_mat_id/ flash-attention always call withnrc = 1.Correctness
Each accumulator sums the same per-block products in the same order as the 1x1 path, so results are bit-identical:
n= 32 / 64 / 256 / 2048 / 5632memcmp)mul_matoutput (4 threads), shapes 256x64x8, 256x63x7, 2048x512x64, 512x66x6, 64x2048x1, 5632x130x3cmp)test-quantize-fns,test-quantize-perf,test-backend-ops -o MUL_MATThe nrc == 2 branch was also instrumented to confirm the mat-mul driver actually reaches it (7736 tile calls across the shapes above).
Compile-checked on
-msse4.2,-msse4.2 -mavx -mf16c -mfma,-mavx2,-march=skylake-avx512and-march=sapphirerapids(no new warnings).Measured effect
CodSpeed simulation run of the macro benchmarks on an AVX2 host, baseline vs this change:
prompt_layer[q8_0]lm_head[q8_0]decode_layer[q8_0]prompt_layer[q4_k],decode_layer[q4_k],decode_deep[q4_k],lm_head[q4_k]decode_layer[q8_0]is a single-token GEMV, where the driver keepsnrc = 1, so it is unchanged as expected. The non-Q8_0 benchmarks are untouched.Note on CI coverage
This is an AVX2-only change, and neither CodSpeed job in this repository can observe it:
simulationjob runsbench_ggml.cpp, whosemul_matmicro benchmarks only coverf32,q4_0andq4_k- there is no Q8_0 mat-mul micro benchmark;walltimemacro job runs on aarch64 macro runners (the flamegraph resolves toggml-cpu/arch/arm/quants.c), where Q8_0 already gets a 2x2mmlakernel wheni8mmis available.So the CodSpeed report on this PR is expected to show no change. The numbers above come from a local CodSpeed simulation run of
ggml-macro-benchmarkson x86 (the same instrument as the CI simulation job, just pointed at the macro suite). Walltime validation could not be done locally: the sandbox lacks thelinux-toolsperf packages the walltime instrument requires.