SSE2双倍乘法比标准乘法慢

时间:2011-07-03 19:25:43

标签: c++ visual-c++ sse sse2

我想知道为什么以下带有SSE2指令的代码执行乘法的速度比标准C ++实现慢。 这是代码:

        m_win = (double*)_aligned_malloc(size*sizeof(double), 16);
        __m128d* pData = (__m128d*)input().data;
        __m128d* pWin = (__m128d*)m_win;
        __m128d* pOut = (__m128d*)m_output.data;
        __m128d tmp;
        int i=0;
        for(; i<m_size/2;i++)
            pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

m_output.datainput().data的内存已分配_aligned_malloc。

然而,对于2 ^ 25阵列执行此代码的时间与此代码的时间(350ms)相同:

for(int i=0;i<m_size;i++)
    m_output.data[i] = input().data[i] * m_win[i];

怎么可能?理论上它应该只占50%的时间,对吧?或者从SIMD寄存器到m_output.data数组的内存传输开销如此昂贵?

如果我替换第一个代码段

中的行
pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

通过

tmp = _mm_mul_pd(pData[i], pWin[i]);

其中__m128d tmp;然后代码执行速度极快,低于我的计时器功能的分辨率。 那是因为一切都只是存储在寄存器而不是存储器中吗?

更令人惊讶的是,如果我在调试模式下编译,SSE代码需要仅93ms ,而标准乘法需要 309ms

  • DEBUG:93ms(SSE2)/ 309ms(标准乘法)
  • RELEASE:350ms(SSE2)/ 350(标准乘法)

这里发生了什么???

我在发布模式下使用MSVC2008和QtCreator 2.2.1。 以下是我的RELEASE编译开关:

cl -c -nologo -Zm200 -Zc:wchar_t- -O2 -MD -GR -EHsc -W3 -w34100 -w34189

这些是DEBUG:

cl -c -nologo -Zm200 -Zc:wchar_t- -Zi -MDd -GR -EHsc -W3 -w34100 -w34189

修改 关于RELEASE vs DEBUG问题: 我只想注意,我在发布模式下对代码进行了分析,而SSE代码实际上更慢了! 这只是在某种程度上证实了VS2008无法正确处理优化器内在函数的假设。 Intel VTune在DEBUG中为SSE循环提供了289ms,在RELEASE模式下为504ms。 哇......哇哇......

2 个答案:

答案 0 :(得分:3)

首先,VS 2008对于inisincs来说是一个糟糕的选择,因为它往往会增加比必要更多的寄存器移动,并且通常不能很好地进行优化(例如,当SSE指令是SSE指令时,它存在环路归纳变量分析的问题本。)

所以,我的猜测是编译器生成mulss指令,CPU可以简单地重新排序并执行并行执行(迭代之间没有依赖关系),而intrisincs导致大量的寄存器移动/复杂的SSE代码 - - 它甚至可能会破坏现代CPU上的跟踪缓存。 VS2008因在寄存器中进行所有计算而臭名昭着,我想CPU会出现一些危险(例如xor reg,移动mem-&gt; reg,xor,mov mem-&gt; reg,mul,mov mem-&gt ; reg是一个依赖链,而标量代码可能是移动mem-&gt; reg,mul和mem操作数,mov。)你一定要查看生成的程序集或尝试VS 2010,其中很多更好地支持intrinsincs。

最后,也是最重要的:你的代码不是计算绑定的 ,没有任何数量的SSE会使它明显加快。在每次迭代中,您正在读取四个双值并写入两个,这意味着FLOP不是您的问题。在这种情况下,你受到缓存/内存子系统的支配,这可能解释了你看到的差异。调试乘法不应该比释放快;如果你看到它比你应该做更多的运行更快并检查还有什么进展(小心如果你的CPU支持turbo模式,那就会增加另外20%的变化。)一个清空缓存的上下文切换可能就足够了这种情况。

所以,总的来说,你所做的测试几乎毫无意义,只是表明对于内存绑定的情况,使用SSE没有区别。如果实际上存在计算密集且并行的代码,则应该使用SSE,即使这样,我也会花很多时间使用分析器来确定优化的确切位置。一个简单的点产品不适合用SSE看到任何性能改进。

答案 1 :(得分:1)

有几点:

  • 正如已经指出的那样,MSVC为SSE生成了非常糟糕的代码
  • 您的代码几乎肯定会限制内存带宽,因为您只在加载和存储之间执行一项操作
  • 大多数现代x86 CPU都有两个浮点ALU,因此即使你没有带宽限制,使用SSE进行双精度浮点运算也几乎无法获得。