ggml-cpu: 2x2 register-blocked kernel for ggml_vec_dot_f16 - #42
Open
codspeed-hq[bot] wants to merge 1 commit into
Open
Conversation
ggml_compute_forward_mul_mat() calls ggml_vec_dot_f16() once per (src0 row, src1 column) pair, so every F16 weight vector is loaded - and, on targets without native f16 arithmetic, converted to f32 - once per column of the tile. Add an nrc == 2 path to ggml_vec_dot_f16() that computes a 2x2 output tile (two src0 rows x two src1 columns) in a single pass and enable nrows = 2 for GGML_TYPE_F16 on the targets using the generic GGML_F16_VEC macros (the SVE and RVV paths keep their own loops and stay at nrows = 1). Each loaded vector feeds two of the four accumulators: 4 loads + 4 FMAs per vector position instead of 8 loads + 4 FMAs, with the accumulators held in named register variables instead of a stack array.
Author
Merging this PR will improve performance by ×2.3
Performance Changes
Tip Curious why this is faster? Comment Comparing |
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()callsggml_vec_dot_f16()once per (src0 row, src1 column) pair, so for an F16 matmul every weight vector is loaded — and, on targets without native f16 arithmetic, converted to f32 — once per column of the tile.This adds an
nrc == 2path toggml_vec_dot_f16()that computes a 2×2 output tile (two src0 rows × two src1 columns) in one pass, and enablesnrows = 2forGGML_TYPE_F16on the targets that use the genericGGML_F16_VECmacros. The SVE and RVV paths have their own hand-written loops and keepnrows = 1.Each loaded vector now feeds two of the four accumulators: 4 loads + 4 FMAs per vector position instead of 8 loads + 4 FMAs. The tile also keeps its operands and accumulators in named register variables instead of the
GGML_F16_VEC ax[GGML_F16_ARR]stack arrays the 1×1 loop uses, which at-O2(theRelWithDebInfobenchmark build) GCC does not unroll — so the accumulator is reloaded from and stored back to the stack around every FMA there. The generated tile loop is pure loads + FMAs, with no spills, on both x86-64 (AVX2/F16C) and aarch64.Measured effect
CodSpeed CPU simulation on the macro benchmarks, base (
46819c9) vs head, same sandbox:lm_head[f16]lm_head[q8_0]lm_head[q4_k]The blast radius is limited to F16 matmuls: no other benchmark in the suite uses an F16
src0, and the two quantizedlm_headvariants are reported unchanged. Wall-time numbers on the macro runner will come from this PR'smacro-benchmarksjob — the walltime instrument could not be run in the sandbox (it needslinux-tools/perf, unavailable there).Correctness
Results are no longer bit-identical to the 1×1 path (the tile accumulates in two groups per output instead of
GGML_F16_ARR), so the change was validated numerically rather than by comparison:nrc = 1— with 1/2/3/8 threads): max relative error 7.3e-7, NMSE 9.0e-14, no failures.vec.cppwith a plain cross-compiler on this branch or on the base commit (pre-existingvector floatvsstd::vectorclash inggml_vec_dot_f16_unroll), so it could not be checked.Note on the POWER9 macro:
GGML_F16_VEC_LOAD(p, i)derives from the parity ofiwhich half of the loaded 16-byte block to convert, so the tile passes the index of the vector within the step (0 for the first half, 1 for the second) rather than an operand id, matching the existing 1×1 loop's usage.One behavioural note for targets with native f16 vector arithmetic (aarch64
+fp16): as before, accumulation happens in f16, but each output now has 2 accumulator chains instead ofGGML_F16_ARR(4), i.e. ~√2 more accumulation error on an already-f16 accumulation.