我有两个double
类型的数组,我想执行vecA += vecB
。到目前为止,我正在做vecA = vecA + vecB
据我所知,例如写i = i + 5
的整数比i += 5
慢。所以我想知道,是否有一些SSE功能只能在__m128d上进行operator+=
。我搜查了一下,一无所获。我的应用程序花费大约60%的时间用于此vecA = vecA + vecB
操作,因此将显示任何性能提升。
下面代码片段中的所有数组均为16字节对齐,len
始终为偶数。
原始代码只是
inline void addToDoubleVectorSSE(
const double * what, const double * toWhat, double * dest, const unsigned int len)
{
__m128d * _what = (__m128d*)what;
__m128d * _toWhat = (__m128d*)toWhat;
for ( register unsigned int i = 0; i < len; i+= 2 )
{
*_toWhat = _mm_add_pd( *_what, *_toWhat );
_what++;
_toWhat++;
}
}
在阅读http://fastcpp.blogspot.cz/2011/04/how-to-process-stl-vector-using-sse.html之后,作者通过不立即写入他刚读的内容来获得表现,我试过了
__m128d * _what = (__m128d*)what;
__m128d * _toWhat = (__m128d*)toWhat;
__m128d * _toWhatBase = (__m128d*)toWhat;
__m128d _dest1;
__m128d _dest2;
for ( register unsigned int i = 0; i < len; i+= 4 )
{
_toWhatBase = _toWhat;
_dest1 = _mm_add_pd( *_what++, *_toWhat++ );
_dest2 = _mm_add_pd( *_what++, *_toWhat++ );
*_toWhatBase++ = _dest1;
*_toWhatBase++ = _dest2;
}
但速度方面没有改善。那么operator+=
是否有__m128d
?或者是否有其他方法可以用于在双精度数组上执行operator + =?使用MSVC,目标平台在Intel i7 CPU上始终是Windows(XP和7)。
答案 0 :(得分:4)
您正在做不必要的工作,现代编译器会自动生成这种代码。该功能称为“自动矢量化”。 MSVC在VS2012中也支持它。我无法理解你的代码,所以我重写了这样:
inline void addToDoubleVectorSSE(
const double * what, double * toWhat, const unsigned int len)
{
for (unsigned ix = 0; ix < len; ++ix)
toWhat[ix] += what[ix];
}
哪个产生了这个机器代码:
00A3102E xor eax,eax
00A31030 movupd xmm0,xmmword ptr [esp+eax+358h]
00A31039 movupd xmm1,xmmword ptr [esp+eax+38h]
00A3103F add eax,10h
00A31042 addpd xmm1,xmm0 // <=== Look!!
00A31046 movupd xmmword ptr [esp+eax+348h],xmm1
00A3104F cmp eax,320h
00A31054 jb wmain+30h (0A31030h)
显然,鉴于代码看起来更干净,你应该支持这个解决方案。如有必要,请更新您的VS版本。
答案 1 :(得分:3)
据我所知,没有等效的+=
,因为SSE算术运算通常是寄存器到寄存器或存储器到寄存器,但不是寄存器到存储器。
但是,您可以使用您链接的博客文章中的建议来改善您的表现。诀窍无法为您效果的原因是您没有消除两条指令之间的依赖关系:++
和_what++
中_toWhat++
增量的副作用会阻止第二对从同一时间开始的操作。按如下方式修改循环以获得改进:
for ( register unsigned int i = 0; i < len; i+= 4, _what += 2, _toWhat += 2, _toWhatBase+=2 )
{
_toWhatBase = _toWhat;
_dest1 = _mm_add_pd( *_what, *_toWhat );
_dest2 = _mm_add_pd( *(_what+1), *(_toWhat+1));
*_toWhatBase = _dest1;
*(_toWhatBase+1) = _dest2;
}
更改后,_dest2
上的操作独立于_dest1
上的操作
根据我的挂钟估计,经过这个简单的修改,我得到了大约28%的改善。