我试图在Intel i3处理器上找到32个元素(每个1字节数据)的总和减少量。我这样做了:
s=0;
for (i=0; i<32; i++)
{
s = s + a[i];
}
然而,由于我的应用程序是一个需要更少时间的实时应用程序,因此需要花费更多时间。 请注意,最终金额可能超过255。
有没有办法可以使用低级SIMD SSE2指令实现这一点?不幸的是我从未使用过SSE。我试图为此目的搜索sse2函数,但它也不可用。 (sse)是否可以保证减少这种小型问题的计算时间?
有什么建议吗?
注意:我已经使用OpenCL和CUDA实现了类似的算法,虽然问题规模很大,但效果很好。对于小型问题,开销成本更高。不确定它在SSE上是如何工作的
答案 0 :(得分:9)
您可以滥用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];
}
答案 1 :(得分:5)
这有点啰嗦,但它应该至少比标量代码快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
改善上述代码。
答案 2 :(得分:0)
还有另一种使用SSE指令查找数组所有元素之和的方法。该代码使用以下SSE构造。
该代码适用于任何大小的浮点数组。
float sse_array_sum(float *a, int size)
{
/*
* sum += a[i] (for all i in domain)
*/
float *sse_sum, sum=0;
if(size >= 8)
{
// sse_sum[8]
posix_memalign((void **)&sse_sum, 32, 8*sizeof(float));
__m256 temp_sum;
__m256* ptr_a = (__m256*)a;
int itrs = size/8-1;
// sse_sum[0:7] = a[0:7]
temp_sum = *ptr_a;
a += 8;
ptr_a++;
for(int i=0; i<itrs; i++, ptr_a++, a+=8)
temp_sum = _mm256_add_ps(temp_sum, *ptr_a);
_mm256_store_ps(sse_sum, temp_sum);
for(int i=0; i<8; i++) sum += sse_sum[i];
}
// if size is not divisible by 8
int rmd_itrs = size%8;
// Note: a is pointing to remainder elements
for(int i=0; i<rmd_itrs; i++) sum += a[i];
return sum;
}
float seq_array_sum(float *a, int size)
{
/*
* sum += a[i] (for all i)
*/
float sum = 0;
for(int i=0; i<size; i++) sum += a[i];
return sum;
}
基准:
大小= 64000000
对于域中的所有i,a [i] = 3141592.65358
顺序版本时间:194ms
SSE版本时间:49毫秒
机器规格:
每个核心的线程数:2
每个插槽的核心数:2
插座:1
CPU MHz:1700.072
操作系统:Ubuntu