ggml-cpu: vectorize the fused RMS-norm × weight kernel - #29
Open
codspeed-hq[bot] wants to merge 1 commit into
Open
Conversation
Vectorize both scalar loops of the fused RMS-norm path (ggml_compute_forward_rms_norm_f32<GGML_RMS_NORM_FUSE_OP_MUL>): - replace the scalar sum-of-squares reduction with ggml_vec_dot_f32, since sum(x*x) == dot(x, x) (same idiom as ggml_vec_norm_f32). - add ggml_vec_scale_mul_f32(n, y, x, w, s) computing y[i] = (x[i]*s)*w[i] using the existing GGML_SIMD idiom (SVE / RISC-V / generic + scalar fallback), and use it for the fused output loop. The two multiplies are kept separate (no FMA) so each product is rounded exactly as in the scalar expression. rms_norm micro-benchmark: 561.2 us -> 325.5 us (simulation), no regressions on the other benchmarks.
Author
Merging this PR will improve performance by 73.53%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | rms_norm |
561.2 µs | 323.4 µs | +73.53% |
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-vectorize-the-fused-rms-norm-weight-kernel-ggml-co-1785215968422 (8f1eb12) 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.
Summary
Vectorizes both scalar loops of the fused RMS-norm path (
ggml_compute_forward_rms_norm_f32<GGML_RMS_NORM_FUSE_OP_MUL>inggml/src/ggml-cpu/ops.cpp). This computesrms_norm(x) * w, the kernel applied before every attention and feed-forward block, so it is a genuine inference hot path.Why this path
The walltime macro benchmarks are dominated by this kernel: on the CodSpeed macro (aarch64) runner,
ggml_compute_forward_rms_norm_mul_fusedaccounts for ~52% of self time inprompt_layer[q4_k]. Therms_normmicro-benchmark exercises the exact same fused template, and its self-time breakdown was ~56% instructions / ~42% memory — a classic unvectorized scalar loop with the vector units idle.Both loops in the fused path were plain scalar (the reduction even carried the code's own
// worth switching to explicit SIMD?comment):y[i] = x[i] * scale * w[i].ggml-cpuis built at-O2where the compiler does not auto-vectorize these, so they ran scalar over the whole hidden dimension for every row.Change
ops.cpp: replaced the scalar sum-of-squares withggml_vec_dot_f32(ne00, &sumf, 0, x, 0, x, 0, 1)sincesum(x*x) == dot(x, x)(the same idiom already used byggml_vec_norm_f32), and replaced the scalar output loop with a new helper.vec.h: addedggml_vec_scale_mul_f32(n, y, x, w, s)computingy[i] = (x[i]*s)*w[i]with the existingGGML_SIMD2-accumulator idiom (SVE / RISC-V / generic paths + scalar fallback), matching the structure ofggml_vec_scale_f32. The two multiplies are kept separate (no FMA) so each product is rounded exactly as in the scalar expression.Validation
Correctness:
test-backend-opsbuilds cleanly; a standalone check ofggml_vec_scale_mul_f32against a double-precision scalar reference over a 2048×37 (odd-row, leftover-exercising) input was bit-identical to the scalar expression (max_abs = 0,max_rel = 0) on the compiled SIMD path.Performance (CodSpeed simulation, this repo's benchmark suite, matched base vs head):
rms_normThe other 14 benchmarks are unchanged — no regressions.