# Fastest way to do horizontal SSE vector sum (or other reduction)

In general for any kind of vector horizontal reduction, extract / shuffle high half to line up with low, then vertical add (or min/max/or/and/xor/multiply/whatever); repeat until a there’s just a single element (with high garbage in the rest of the vector).

If you start with vectors wider than 128-bit, narrow in half until you get to 128 (then you can use one of the functions in this answer on that vector). But if you need the result broadcast to all elements at the end, then you can consider doing full-width shuffles all the way.

Related Q&As for wider vectors, and integers, and FP

• `__m128` and `__m128d` This answer (see below)

• `__m256d` with perf analysis for Ryzen 1 vs. Intel (showing why `vextractf128` is vastly better than `vperm2f128`) Get sum of values stored in __m256d with SSE/AVX

• `__m256` How to sum __m256 horizontally?

• Intel AVX: 256-bits version of dot product for double precision floating point variables of single vectors.

• Dot product of arrays (not just a single vector of 3 or 4 elements): do vertical mul/add or FMA into multiple accumulators, and hsum at the end. Complete AVX+FMA array dot-product example, including an efficient hsum after the loop. (For the simple sum or other reduction of an array, use that pattern but without the multiply part, e.g. add instead of fma). Do not do the horizontal work separately for each SIMD vector; do it once at the end.

How to count character occurrences using SIMD as an integer example of counting `_mm256_cmpeq_epi8` matches, again over a whole array, only hsumming at the end. (Worth special mention for doing some 8-bit accumulation then widening 8 -> 64-bit to avoid overflow without doing a full hsum at that point.)

Integer

• `__m128i` 32-bit elements: this answer (see below). 64-bit elements should be obvious: only one pshufd/paddq step.

• `__m128i` 8-bit unsigned `uint8_t` elements without wrapping/overflow: `psadbw` against `_mm_setzero_si128()`, then hsum the two qword halves (or 4 or 8 for wider vectors). Fastest way to horizontally sum SSE unsigned byte vector shows 128-bit with SSE2.
Summing 8-bit integers in __m512i with AVX intrinsics has an AVX512 example. How to count character occurrences using SIMD has an AVX2 `__m256i` example.

(For `int8_t` signed bytes you can XOR set1_epi8(0x80) to flip to unsigned before SAD, then subtract the bias from the final hsum; see details here, also showing an optimization for doing only 9 bytes from memory instead of 16).

• 16-bit unsigned: `_mm_madd_epi16` with set1_epi16(1) is a single-uop widening horizontal add: SIMD: Accumulate Adjacent Pairs. Then proceed with a 32-bit hsum.

• `__m256i` and `__m512i` with 32-bit elements.
Fastest method to calculate sum of all packed 32-bit integers using AVX512 or AVX2. For AVX512, Intel added a bunch of “reduce” inline functions (not hardware instructions) that do this for you, like `_mm512_reduce_add_ps` (and pd, epi32, and epi64). Also reduce_min/max/mul/and/or. Doing it manually leads to basically the same asm.

• horizontal max (instead of add): Getting max value in a __m128i vector with SSE?

### Main answer to this question: mostly float and `__m128`

Here are some versions tuned based on Agner Fog’s microarch guide’s microarch guide and instruction tables. See also the x86 tag wiki. They should be efficient on any CPU, with no major bottlenecks. (e.g. I avoided things that would help one uarch a bit but be slow on another uarch). Code-size is also minimized.

The common SSE3 / SSSE3 2x `hadd` idiom is only good for code-size, not speed on any existing CPUs. There are use-cases for it (like transpose and add, see below), but a single vector isn’t one of them.

I’ve also included an AVX version. Any kind of horizontal reduction with AVX / AVX2 should start with a `vextractf128` and a “vertical” operation to reduce down to one XMM (`__m128`) vector. In general for wide vectors, your best bet is to narrow in half repeatedly until you’re down to a 128-bit vector, regardless of element type. (Except for 8-bit integer, then `vpsadbw` as a first step if you want to hsum without overflow to wider elements.)

See the asm output from all this code on the Godbolt Compiler Explorer. See also my improvements to Agner Fog’s C++ Vector Class Library `horizontal_add` functions. (message board thread, and code on github). I used CPP macros to select optimal shuffles for code-size for SSE2, SSE4, and AVX, and for avoiding `movdqa` when AVX isn’t available.

• code size: smaller is better for L1 I-cache reasons, and for code fetch from disk (smaller binaries). Total binary size mostly matters for compiler decisions made repeatedly all over a program. If you’re bothering to hand-code something with intrinsics, it’s worth spending a few code bytes if it gives any speedup for the whole program (be careful of microbenchmarks that make unrolling look good).
• uop-cache size: Often more precious than L1 I\$. 4 single-uop instructions can take less space than 2 `haddps`, so this is highly relevant here.
• latency: Sometimes relevant
• throughput (back-end ports): usually irrelevant, horizontal sums shouldn’t be the only thing in an innermost loop. Port pressure matters only as part of the whole loop that contains this.
• throughput (total front-end fused-domain uops): If surrounding code doesn’t bottleneck on the same port that the hsum uses, this is a proxy for the impact of the hsum on the throughput of the whole thing.

When a horizontal add is infrequent:

CPUs with no uop-cache might favour 2x `haddps` if it’s very rarely used: It’s slowish when it does run, but that’s not often. Being only 2 instructions minimizes the impact on the surrounding code (I\$ size).

CPUs with a uop-cache will probably favour something that takes fewer uops, even if it’s more instructions / more x86 code-size. Total uops cache-lines used is what we want to minimize, which isn’t as simple as minimizing total uops (taken branches and 32B boundaries always start a new uop cache line).

Anyway, with that said, horizontal sums come up a lot, so here’s my attempt at carefully crafting some versions that compile nicely. Not benchmarked on any real hardware, or even carefully tested. There might be bugs in the shuffle constants or something.

If you’re making a fallback / baseline version of your code, remember that only old CPUs will run it; newer CPUs will run your AVX version, or SSE4.1 or whatever.

Old CPUs like K8, and Core2(merom) and earlier only have 64bit shuffle units. Core2 has 128bit execution units for most instructions, but not for shuffles. (Pentium M and K8 handle all 128b vector instructions as two 64bit halves).

Shuffles like `movhlps` that move data in 64-bit chunks (no shuffling within 64-bit halves) are fast, too.

Related: shuffles on new CPUs, and tricks for avoiding 1/clock shuffle throughput bottleneck on Haswell and later: Do 128bit cross lane operations in AVX512 give better performance?

On old CPUs with slow shuffles:

• `movhlps` (Merom: 1uop) is significantly faster than `shufps` (Merom: 3uops). On Pentium-M, cheaper than `movaps`. Also, it runs in the FP domain on Core2, avoiding the bypass delays from other shuffles.
• `unpcklpd` is faster than `unpcklps`.
• `pshufd` is slow, `pshuflw`/`pshufhw` are fast (because they only shuffle a 64bit half)
• `pshufb mm0` (MMX) is fast, `pshufb xmm0` is slow.
• `haddps` is very slow (6uops on Merom and Pentium M)
• `movshdup` (Merom: 1uop) is interesting: It’s the only 1uop insn that shuffles within 64b elements.

`shufps` on Core2(including Penryn) brings data into the integer domain, causing a bypass delay to get it back to the FP execution units for `addps`, but `movhlps` is entirely in the FP domain. `shufpd` also runs in the float domain.

`movshdup` runs in the integer domain, but is only one uop.

AMD K10, Intel Core2(Penryn/Wolfdale), and all later CPUs, run all xmm shuffles as a single uop. (But note the bypass delay with `shufps` on Penryn, avoided with `movhlps`)

Without AVX, avoiding wasted `movaps`/`movdqa` instructions requires careful choice of shuffles. Only a few shuffles work as a copy-and-shuffle, rather than modifying the destination. Shuffles that combine data from two inputs (like `unpck*` or `movhlps`) can be used with a tmp variable that’s no longer needed instead of `_mm_movehl_ps(same,same)`.

Some of these can be made faster (save a MOVAPS) but uglier / less “clean” by taking a dummy arg for use as a destination for an initial shuffle. For example:

``````// Use dummy = a recently-dead variable that vec depends on,
//  so it doesn't introduce a false dependency,
//  and the compiler probably still has it in a register
__m128d highhalf_pd(__m128d dummy, __m128d vec) {
#ifdef __AVX__
// With 3-operand AVX instructions, don't create an extra dependency on something we don't need anymore.
(void)dummy;
return _mm_unpackhi_pd(vec, vec);
#else
// Without AVX, we can save a MOVAPS with MOVHLPS into a dead register
__m128 tmp = _mm_castpd_ps(dummy);
__m128d high = _mm_castps_pd(_mm_movehl_ps(tmp, _mm_castpd_ps(vec)));
return high;
#endif
}
``````

## SSE1 (aka SSE):

``````float hsum_ps_sse1(__m128 v) {                                  // v = [ D C | B A ]
__m128 shuf   = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1));  // [ C D | A B ]
__m128 sums   = _mm_add_ps(v, shuf);      // sums = [ D+C C+D | B+A A+B ]
shuf          = _mm_movehl_ps(shuf, sums);      //  [   C   D | D+C C+D ]  // let the compiler avoid a mov by reusing shuf
return    _mm_cvtss_f32(sums);
}
# gcc 5.3 -O3:  looks optimal
movaps  xmm1, xmm0     # I think one movaps is unavoidable, unless we have a 2nd register with known-safe floats in the upper 2 elements
shufps  xmm1, xmm0, 177
movhlps xmm1, xmm0     # note the reuse of shuf, avoiding a movaps

# clang 3.7.1 -O3:
movaps  xmm1, xmm0
shufps  xmm1, xmm1, 177
movaps  xmm0, xmm1
shufpd  xmm0, xmm0, 1
``````

I reported a clang bug about pessimizing the shuffles. It has its own internal representation for shuffling, and turns that back into shuffles. gcc more often uses the instructions that directly match the intrinsic you used.

Often clang does better than gcc, in code where the instruction choice isn’t hand-tuned, or constant-propagation can simplify things even when the intrinsics are optimal for the non-constant case. Overall it’s a good thing that compilers work like a proper compiler for intrinsics, not just an assembler. Compilers can often generate good asm from scalar C that doesn’t even try to work the way good asm would. Eventually compilers will treat intrinsics as just another C operator as input for the optimizer.

## SSE3

``````float hsum_ps_sse3(__m128 v) {
__m128 shuf = _mm_movehdup_ps(v);        // broadcast elements 3,1 to 2,0
shuf        = _mm_movehl_ps(shuf, sums); // high half -> low half
return        _mm_cvtss_f32(sums);
}

# gcc 5.3 -O3: perfectly optimal code
movshdup    xmm1, xmm0
movhlps     xmm1, xmm0
``````

• doesn’t require any `movaps` copies to work around destructive shuffles (without AVX): `movshdup xmm1, xmm2`‘s destination is write-only, so it creates `tmp` out of a dead register for us. This is also why I used `movehl_ps(tmp, sums)` instead of `movehl_ps(sums, sums)`.

• small code-size. The shuffling instructions are small: `movhlps` is 3 bytes, `movshdup` is 4 bytes (same as `shufps`). No immediate byte is required, so with AVX, `vshufps` is 5 bytes but `vmovhlps` and `vmovshdup` are both 4.

I could save another byte with `addps` instead of `addss`. Since this won’t be used inside inner loops, the extra energy to switch the extra transistors is probably negligible. FP exceptions from the upper 3 elements aren’t a risk, because all elements hold valid FP data. However, clang/LLVM actually “understands” vector shuffles, and emits better code if it knows that only the low element matters.

Like the SSE1 version, adding the odd elements to themselves may cause FP exceptions (like overflow) that wouldn’t happen otherwise, but this shouldn’t be a problem. Denormals are slow, but IIRC producing a +Inf result isn’t on most uarches.

## SSE3 optimizing for code-size

If code-size is your major concern, two `haddps` (`_mm_hadd_ps`) instructions will do the trick (Paul R’s answer). This is also the easiest to type and remember. It is not fast, though. Even Intel Skylake still decodes each `haddps` to 3 uops, with 6 cycle latency. So even though it saves machine-code bytes (L1 I-cache), it takes up more space in the more-valuable uop-cache. Real use-cases for `haddps`: a transpose-and-sum problem, or doing some scaling at an intermediate step in this SSE `atoi()` implementation.

## AVX:

This version saves a code byte vs. Marat’s answer to the AVX question.

``````#ifdef __AVX__
float hsum256_ps_avx(__m256 v) {
__m128 vlow  = _mm256_castps256_ps128(v);
__m128 vhigh = _mm256_extractf128_ps(v, 1); // high 128
return hsum_ps_sse3(vlow);         // and inline the sse3 version, which is optimal for AVX
// (no wasted instructions, and all of them are the 4B minimum)
}
#endif

vmovaps xmm1,xmm0               # huh, what the heck gcc?  Just extract to xmm1
vextractf128 xmm0,ymm0,0x1
vmovshdup xmm1,xmm0
vmovhlps xmm1,xmm1,xmm0
vzeroupper
ret
``````

## Double-precision:

``````double hsum_pd_sse2(__m128d vd) {                      // v = [ B | A ]
__m128 undef  = _mm_undefined_ps();                       // don't worry, we only use addSD, never touching the garbage bits with an FP add
__m128 shuftmp= _mm_movehl_ps(undef, _mm_castpd_ps(vd));  // there is no movhlpd
__m128d shuf  = _mm_castps_pd(shuftmp);
}

# gcc 5.3.0 -O3
pxor    xmm1, xmm1          # hopefully when inlined, gcc could pick a register it knew wouldn't cause a false dep problem, and avoid the zeroing
movhlps xmm1, xmm0

# clang 3.7.1 -O3 again doesn't use movhlps:
xorpd   xmm2, xmm2          # with  #define _mm_undefined_ps _mm_setzero_ps
movapd  xmm1, xmm0
unpckhpd        xmm1, xmm2
movapd  xmm0, xmm1    # another clang bug: wrong choice of operand order

// This doesn't compile the way it's written
double hsum_pd_scalar_sse2(__m128d vd) {
double tmp;
_mm_storeh_pd(&tmp, vd);       // store the high half
double lo = _mm_cvtsd_f64(vd); // cast the low half
return lo+tmp;
}

# gcc 5.3 -O3
haddpd  xmm0, xmm0   # Lower latency but less throughput than storing to memory

# ICC13
movhpd    QWORD PTR [-8+rsp], xmm0    # only needs the store port, not the shuffle unit
``````

Storing to memory and back avoids an ALU uop. That’s good if shuffle port pressure, or ALU uops in general, are a bottleneck. (Note that it doesn’t need to `sub rsp, 8` or anything because the x86-64 SysV ABI provides a red-zone that signal handlers won’t step on.)

Some people store to an array and sum all the elements, but compilers usually don’t realize that the low element of the array is still there in a register from before the store.

## Integer:

`pshufd` is a convenient copy-and-shuffle. Bit and byte shifts are unfortunately in-place, and `punpckhqdq` puts the high half of the destination in the low half of the result, opposite of the way `movhlps` can extract the high half into a different register.

Using `movhlps` for the first step might be good on some CPUs, but only if we have a scratch reg. `pshufd` is a safe choice, and fast on everything after Merom.

``````int hsum_epi32_sse2(__m128i x) {
#ifdef __AVX__
__m128i hi64  = _mm_unpackhi_epi64(x, x);           // 3-operand non-destructive AVX lets us save a byte without needing a mov
#else
__m128i hi64  = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
#endif
__m128i hi32  = _mm_shufflelo_epi16(sum64, _MM_SHUFFLE(1, 0, 3, 2));    // Swap the low two elements
return _mm_cvtsi128_si32(sum32);       // SSE2 movd
//return _mm_extract_epi32(hl, 0);     // SSE4, even though it compiles to movd instead of a literal pextrd r32,xmm,0
}

# gcc 5.3 -O3
pshufd xmm1,xmm0,0x4e