This is a bit long-winded but it should still be at least 2x faster than the scalar code:
uint16_t sum_32(const uint8_t a[32])
{
const __m128i vk0 = _mm_set1_epi8(0); // constant vector of all 0s for use with _mm_unpacklo_epi8/_mm_unpackhi_epi8
__m128i v = _mm_load_si128(a); // load first vector of 8 bit values
__m128i vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
__m128i vh = _mm_unpackhi_epi8(v, vk0);
__m128i vsum = _mm_add_epi16(vl, vh);
v = _mm_load_si128(&a[16]); // load second vector of 8 bit values
vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
vh = _mm_unpackhi_epi8(v, vk0);
vsum = _mm_add_epi16(vsum, vl);
vsum = _mm_add_epi16(vsum, vh);
// horizontal sum
vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 8));
vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 4));
vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 2));
return _mm_extract_epi16(vsum, 0);
}
Note that a[]
needs to be 16 byte aligned.
You can probably improve on the above code using _mm_hadd_epi16
.