总和减少没有溢出的无符号字节,采用英特尔SSE2总和减少没有溢出的无符号字节,采用英特尔SSE2(S

2019-05-12 08:48发布

我试图找到32个元件(每个1个字节的数据)的Intel i3处理器的总和减少。 我这样做:

s=0; 
for (i=0; i<32; i++)
{
    s = s + a[i];
}  

然而,它的花费更多的时间,因为我的应用程序是需要少得多的时间内实时应用。 请注意,最后的总和可能会超过255个。

有没有一种方法,我可以实现这个使用低级别SIMD SSE2指令集? 不幸的是我从来没有使用SSE。 我试图寻找此目的SSE2功能,但它也无法使用。 难道(SSE)保证减少计算时间,这样一个小规模的问题?

有什么建议??

注:我已经实现了使用OpenCL和CUDA类似的算法和伟大的工作,但只有当问题规模是很大的。 对于小型的问题开销的费用为多。 不知道它是如何工作的SSE

Answer 1:

你可以滥用PSADBW快速计算小的水平款项。

事情是这样的:(未测试)

pxor xmm0, xmm0
psadbw xmm0, [a + 0]
pxor xmm1, xmm1
psadbw xmm1, [a + 16]
paddw xmm0, xmm1
pshufd xmm1, xmm0, 2
paddw xmm0, xmm1 ; low word in xmm0 is the total sum

尝试内部函数的版本:

我从来没有使用内在所以这段代码可能是没有任何意义。 拆卸看起来不错,但。

uint16_t sum_32(const uint8_t a[32])
{
    __m128i zero = _mm_xor_si128(zero, zero);
    __m128i sum0 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(a)));
    __m128i sum1 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(&a[16])));
    __m128i sum2 = _mm_add_epi16(sum0, sum1);
    __m128i totalsum = _mm_add_epi16(sum2, _mm_shuffle_epi32(sum2, 2));
    return totalsum.m128i_u16[0];
}


Answer 2:

这是一个有点啰嗦,但它应该仍然比标量代码至少快两倍:

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);
}

需要注意的是a[]需要进行16字节对齐。

您可以在上面的代码中使用可能提高_mm_hadd_epi16



文章来源: Sum reduction of unsigned bytes without overflow, using SSE2 on Intel