【发布时间】:2016-12-24 02:09:19
【问题描述】:
我是 SSE 编程的新手,所以我希望有人可以帮助我。我最近使用 GCC SSE 内在函数实现了一个函数来计算 32 位整数数组的总和。我的实现代码如下。
int ssum(const int *d, unsigned int len)
{
static const unsigned int BLOCKSIZE=4;
unsigned int i,remainder;
int output;
__m128i xmm0, accumulator;
__m128i* src;
remainder = len%BLOCKSIZE;
src = (__m128i*)d;
accumulator = _mm_loadu_si128(src);
output = 0;
for(i=BLOCKSIZE;i<len-remainder;i+=BLOCKSIZE){
xmm0 = _mm_loadu_si128(++src);
accumulator = _mm_add_epi32(accumulator,xmm0);
}
accumulator = _mm_add_epi32(accumulator, _mm_srli_si128(accumulator, 8));
accumulator = _mm_add_epi32(accumulator, _mm_srli_si128(accumulator, 4));
output = _mm_cvtsi128_si32(accumulator);
for(i=len-remainder;i<len;i++){
output += d[i];
}
return output;
}
如您所见,这是一个相当直接的实现,我使用扩展的 xmm 寄存器一次对数组 4 求和,然后在最后通过将剩余元素相加来进行清理。
然后,我将这个 SIMD 实现的性能与一个普通的 for 循环进行了比较。该实验的结果可在此处获得:
如您所见,与 for 循环相比,这个实现确实显示了大约 60% 的加速,输入大小(即数组的长度)高达约 5M 元素。但是,对于较大的输入大小值,与 for 循环相关的性能会急剧下降,并且只产生大约 20% 的加速。
我无法解释这种性能的急剧下降。我或多或少地在内存中线性步进,因此缓存未命中和页面错误的影响对于两种实现来说应该大致相同。我在这里想念什么?有什么办法可以使这条曲线变平?任何想法将不胜感激。
【问题讨论】:
-
你用的是什么CPU?
-
首先,您是否检查了 gcc 是否自动矢量化了标量代码?其次,您可能会受到内存带宽的限制。
-
正如@EOF 所说,您在循环中几乎没有做任何事情(一条 SIMD 算术指令),因此当您拥有大型数组时,您很可能会受到内存带宽的限制。
-
@RomanKhimov 我在我可以访问的各种服务器上都看到了这种现象,但这个特定的实验是在 Intel(R) Xeon(R) CPU E5-2667 v4 @ 3.20GHz w。 504 GB 内存。
-
您应该查看实时时间,而不仅仅是相对时间。当然,内存带宽、缓存和其他开销(包括 VM 实现)对这两种基准的影响是相同的,但这意味着它们对速度更快的基准的影响会成比例地增加。
标签: c performance sse simd intrinsics