2

我有两个double类型的数组,我想执行vecA += vecB. 到目前为止,我正在做vecA = vecA + vecB,据我所知,例如整数写入i = i + 5i += 5. 所以我想知道,是否有一些 SSE 功能可以operator+=在 __m128d 上执行。我搜索并一无所获。我的应用程序在此操作上花费了大约 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)。

4

2 回答 2

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 版本。

于 2013-02-27T23:00:49.210 回答
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% 的改进。

于 2013-02-27T22:44:22.813 回答