Repository navigation
perf: float32 sum blocks on SSE2 and NEON; integer widening folds without bounds checks - #54
Merged
Merged
Conversation
NumPy's pairwise sum keeps eight accumulators so that the inner loop can be vectorised "without changing summation ordering". AddBlocks8F32 is that loop: blocks of eight float32 added lane by lane into r, one IEEE float32 addition per lane per block, in order; SSE2 on amd64 (ADDPS on two XMM), NEON on arm64 (VFADD .S4 on two V), the scalar loop elsewhere. pairwiseSum calls it, so float32 sums are unchanged bit for bit. Tests: kernel vs scalar bit for bit over 1-40 blocks with NaN, the infinities, -0, subnormals and overflow; refusals; a PROT_NONE fence on both operands. Proven to bite: a wrong-register kernel fails the match test, a kernel reading one block too many faults in the fence. Run on arm64 natively and on linux/amd64 (docker). Coverage 100%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ration On amd64 the single loop reloaded the operation from the stack every element, and the A/B on the hosted EPYC showed int32 Sum 48-56% slower on this branch with byte-identical loop code: the new kernel code moved the loop across a 64-byte line. Four independent accumulators (exact: integer + and * wrap modulo 2^64, so order changes nothing) and one loop per operation make it faster and far less sensitive to where it lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Indexing s[i+1..i+3] left four bounds checks an iteration in the amd64 code (the compiler proved none of them); consuming the slice under len(s) >= 4 proves all four. Disassembly: the unrolled int32 loop is now four MOVSXD loads and no check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the first A/B exposed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d a Zen 3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
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.
float32 sums. NumPy's pairwise sum keeps eight accumulators so that the inner loop can be vectorised "without changing summation ordering".
kernels.AddBlocks8F32is that loop: blocks of eight float32 added lane by lane, one IEEE float32 addition per lane per block, in order.ADDPSon two XMM registers), arm64 uses NEON (VFADD .S4on two V registers), and the other architectures keep the scalar loop.pairwiseSumcalls it, so float32 sums are unchanged bit for bit.Tests:
PROT_NONEfence guards both operands.Measured with the new
benchworkflow on the hosted runners: basemainand head alternated 10 times, one thread,benchstat, p ≤ 0.002.Sum, 2¹⁴Sum, 2²⁰Sum, 2¹⁴Sum, 2²⁰A defect the first A/B exposed. With only the kernel added, int32
Sumwas 48–56% slower on amd64 (reproduced with 10 runs), even though its code had not changed.i+1…i+3, and the loop has none.Left: ppc64le, s390x, loong64 and riscv64 keep the scalar block loop. ppc64le needs
xvaddsp, which Go's assembler lacks; go-asmgen/asmgen#27 (draft) adds the VSX single-precision family to its registry, with references from GNU as and llvm-mc.🤖 Generated with Claude Code