ggml-cpu: write matmul results straight into dst in the nrows==1 fast path - #50
Open
codspeed-hq[bot] wants to merge 1 commit into
Conversation
… path Add a dedicated path in ggml_compute_forward_mul_mat_one_chunk() for num_rows_per_vec_dot == 1, which covers every kernel except the ARM mmla ones. It writes the vec_dot results directly into dst instead of staging them in a tmp buffer and memcpy-ing one column at a time, passes constant vec_dot arguments instead of rebuilding them from conditionals on every call, and carries the (i11, i12, i13) column indices across a row block instead of recomputing them with 64-bit divisions per column. The generic num_rows_per_vec_dot > 1 path is unchanged, and 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
Every output element of a CPU matmul goes through
ggml_compute_forward_mul_mat_one_chunk(). The loop around thevec_dotcall carries per-element overhead unrelated to the arithmetic:float tmp[32]buffer and copied todstwith a libcmemcpycall per column,vec_dotarguments are rebuilt fromnum_rows_per_vec_dot > 1 ? … : …conditionals on every call (3cmovs per call),(i11, i12, i13)column indices are recomputed with two 64-bit divisions per column.num_rows_per_vec_dot == 1for every kernel except the ARM mmla ones, so this adds a dedicated fast path for it:dst— a chunk owns those elements exclusively, and the existing comment notes the staging buffer (a false-sharing experiment) "does not seem to make a difference",vec_dotarguments become compile-time constants,The generic
num_rows_per_vec_dot > 1path is untouched.Correctness — bit-identical
The arithmetic is unchanged (same kernel, same operands, same order). Verified against a build of the unmodified tree: 160 cases byte-for-byte identical — F32/F16/Q8_0/Q4_0/Q4_K × 8 shapes (including odd
M/N/K, partial blocks, single-column shapes and broadcastne2/ne3) × 1/2/5/8 threads.Test suite (built from this branch, CPU backend):
test-backend-ops test -o MUL_MAT— OKtest-opt,test-quantize-fns,test-rope,test-barrier,test-alloc,test-gguf,test-col2im-1d— all passMeasured (CodSpeed, CPU simulation, micro suite)
Simulation is deterministic here — 11 of the 15 benchmarks are byte-identical between the two runs, so the noise floor is ~0.
mul_mat[q4_k]mul_mat[q4_0]mul_mat[f32]ffn_swigluNo regressions on any of the 15 micro benchmarks.
Caveats
vec_dotcall does far more work per call).divcosts ~30 cycles but counts as one instruction, and a libcmemcpycall counts as a handful. The walltime macro job should therefore show a larger gain than the numbers above.