0

我想知道为什么带有 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.data和的内存input().data已使用 _aligned_malloc 分配。

然而,对于 2^25 数组执行此代码的时间与此代码的时间相同(350 毫秒):

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

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

这里发生了什么???

我在发布模式下使用带有 QtCreator 2.2.1 的 MSVC2008。这是我的 RELEASE 编译器开关:

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

这些是用于调试的:

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

编辑 关于 RELEASE 与 DEBUG 问题:我只想指出我分析了代码,而 SSE 代码在发布模式下实际上速度较慢! 这只是以某种方式证实了 VS2008 无法正确处理优化器的内在函数的假设。英特尔 VTune 在 DEBUG 中为 SSE 循环提供了 289 毫秒,在 RELEASE 模式下为我提供了 504 毫秒。哇...只是哇...

4

2 回答 2

3

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

所以,我的疯狂猜测是编译器生成mulss指令,CPU 可以简单地重新排序和并行执行(迭代之间没有依赖关系),而 intrisincs 导致大量寄存器移动/复杂的 SSE 代码——它甚至可能破坏跟踪缓存在现代 CPU 上。VS2008 因在寄存器中进行所有计算而臭名昭著,我想会有一些 CPU 无法跳过的危险(例如 xor reg, move mem->reg, xor, mov mem->reg, mul, mov mem->reg which是一个依赖链,而标量代码可能是 move mem->reg, mul with mem operand, mov。)您绝对应该查看生成的程序集或尝试 VS 2010,它对 intrinsincs 有更好的支持。

最后,也是最重要的一点:您的代码根本不受计算限制再多的 SSE 也不会使它显着加快。在每次迭代中,您读取四个双精度值并写入两个,这意味着 FLOP 不是您的问题。在这种情况下,您将受到缓存/内存子系统的支配,这可能解释了您看到的差异。调试乘法不应该比发布快;如果你发现它比你应该做更多的运行速度更快,并检查发生了什么(如果你的 CPU 支持涡轮模式,请小心,这会增加另外 20% 的变化。)清空缓存的上下文切换可能就足够了这个案例。

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

于 2011-07-03T19:37:00.083 回答
1

几点:

  • 正如已经指出的那样,MSVC 为 SSE 生成了非常糟糕的代码
  • 您的代码几乎肯定是内存带宽有限的,因为您在加载和存储之间只执行一项操作
  • 大多数现代 x86 CPU 都有两个浮点 ALU,因此即使您不受带宽限制,使用 SSE 进行双精度浮点数学也可能没什么收获
于 2011-07-03T20:05:46.500 回答