Skip to content

Implement reduce_sum() - #357

Merged
Shnatsel merged 1 commit into
linebender:mainfrom
Shnatsel:reduce-sum
Aug 30, 2026
Merged

Implement reduce_sum()#357
Shnatsel merged 1 commit into
linebender:mainfrom
Shnatsel:reduce-sum

Conversation

@Shnatsel

Copy link
Copy Markdown
Contributor

This implements the hardest part of #340

With floats this is very tricky. Their addition is non-trivial due to accumulating precision loss, and there are many algorithms and many precision trade-offs. See https://orlp.net/blog/taming-float-sums/ for more info.

This PR's implementation splits vectors in half and performs lane-wise addition down to 128-bit vectors, then reduces the 128-bit vector down to a single value with pairwise summation. This is a fast and precise-ish algorithm with log2(N) roundings.

I feel this is a good default because it is portable (bit-exact output everywhere, except for NaN payloads) and pretty fast. This leaves us space to add a _relaxed variant if higher-performance options with fewer guarantees are ever discovered, and/or a _precise variant for Kahann summation with a single rounding instead of log2(N) roundings.

We handily beat std::simd which sums floats one by one left to right, which is awful for both precision and performance: it forces a long chain of scalar additions, and this is the worst case for precision too, causing N roundings for st::simd as opposed to log2(N) in this PR.

Integers are not under any of the same constraints, so I tried looking for faster formulations for integers, but couldn't find anything. This reinforces my hunch that this is about as fast as this op can get, even for floats.

cc @Mnwa who requested this feature

match (vec_ty.scalar, vec_ty.scalar_bits) {
(ScalarType::Float, 32) => self.kernel_method(op, vec_ty, |_| {
quote! {
// _mm_hadd_ps is slower than shuffle followed by add

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why does it exist in the first place? 😂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like historical reasons to me.

It was added in ssse3 while shuffles appeared in SSE4, and it doesn't need an extra register for a shuffle mask.

@Shnatsel
Shnatsel added this pull request to the merge queue Aug 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Aug 30, 2026
Shnatsel added a commit to Shnatsel/fearless_simd that referenced this pull request Aug 30, 2026
…ender#358)

The simpler part of
linebender#340

Implementation decisions largely mirror linebender#357 but we don't have to worry
about error accumulation in floats. API design follows the existing
lane-wise min/max.

cc @Mnwa who requested this feature
@Shnatsel
Shnatsel enabled auto-merge August 30, 2026 09:39
@Shnatsel
Shnatsel added this pull request to the merge queue Aug 30, 2026
Merged via the queue into linebender:main with commit 30c7f38 Aug 30, 2026
22 checks passed
@Shnatsel
Shnatsel deleted the reduce-sum branch August 30, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants