ggml-cpu: keep the generic SIMD vec kernel accumulators in registers - #40
Open
codspeed-hq[bot] wants to merge 1 commit into
Conversation
The generic GGML_SIMD kernels keep their vector operands and accumulators in small arrays indexed by the inner loop counter. Those arrays only stay in registers if the loop is unrolled, which GCC does at -O3 but not at -O2, so RelWithDebInfo builds pay a stack load/store round trip for every vector operation (and re-zero the accumulator array on every call to ggml_vec_dot_f32). Add a GGML_UNROLL(n) helper and apply it to the constant-trip inner loops of the generic kernels, making their codegen independent of the optimization level. No arithmetic is changed and results are bit-identical; -O3 output is unchanged.
Author
Merging this PR will improve performance by 40.8%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | mul_mat[f32] |
346.1 ms | 232.6 ms | +48.81% |
| ⚡ | Simulation | ffn_swiglu |
1,067.7 ms | 727.9 ms | +46.69% |
| ⚡ | WallTime | lm_head[f16] |
60.6 ms | 42.6 ms | +42.19% |
| ⚡ | WallTime | prompt_layer[f32] |
247.1 ms | 195.1 ms | +26.62% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing codspeed-optim-keep-the-simd-vec-kernel-accumulators-in-registers-1785335630636 (0ebdd46) with master (46819c9)
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.
Problem
The generic
GGML_SIMDkernels invec.h/vec.cpphold their vector operands and accumulators in small arrays indexed by the inner loop counter:Those arrays only end up in registers if the inner loop is unrolled. GCC unrolls it at
-O3, but not at-O2- which is whatRelWithDebInfobuilds use, including the benchmark jobs. At-O2every FMA round-trips its accumulator through the stack, andggml_vec_dot_f32additionally re-zeroes its 128-byte accumulator array with arep stoson every call (i.e. once per matmul output element).Change
A new
GGML_UNROLL(n)helper (#pragma GCC unroll n, no-op on compilers that do not support it) applied to the constant-trip inner loops of the generic SIMD kernels:ggml_vec_dot_f32,ggml_vec_dot_f16,ggml_vec_dot_f16_unroll,ggml_vec_mad_f32/_f16/_f32_unroll,ggml_vec_mad1_f32,ggml_vec_scale_f32/_f16.+28 lines, 0 deletions. No arithmetic is touched: the operand partitioning, the number of accumulator groups and the reduction order are all unchanged - the accumulators simply stop living in memory. The hand-written SVE and RISC-V paths are untouched.
Verified on the built objects (x86-64, AVX2,
-O2),ggml_vec_dot_f32:rep stos(accumulator re-zeroing per call)vfmaddin the vector loopMeasured impact
CodSpeed CPU simulation, base vs head of this branch, same build configuration as the
codspeed.ymlmicro job:mul_mat[f32]ffn_swigluThe other 13 micro benchmarks are unchanged (the quantized kernels live in
arch/*/quants.cand do not go through these code paths). The macro walltime suite exercises the same kernels -prompt_layer[f32]is ~99%ggml_vec_dot_f32, andlm_head[f16]goes throughggml_vec_dot_f16- and is measured by CI on this PR.Correctness
rms_norm+mul,soft_max,silu,scaleand 600 dot-product sizes (K = 1 ... 600, so the odd/leftover tails are exercised) were dumped from a build of the base commit and from the patched build; the two dumps compare byte-for-byte equal.test-quantize-fns,test-ropeandtest-barrierpass.test-backend-opsreports OK (it skips the CPU backend when no accelerator device is present).-O3builds are unaffected:objdumpofvec.cppcompiled at-O3before and after the change is identical apart from the object file name, so builds that already use-O3keep exactly the code they have today.-Wall -Wextra -Wpedantic -Wcast-qual -Wextra-semi ...), and it also cross-compiles clean for aarch64 with both-march=armv8.2-a+fp16+dotprodand+sve.Trade-off worth knowing
Because
-O3already produces this code, the win applies to builds compiled at-O2(RelWithDebInfo), which is what the benchmark jobs use on both the simulation and the walltime macro runners. An alternative fix would be to build the benchmark targets with-O3 -g; this change instead makes the kernels insensitive to the optimization level.