【发布时间】:2011-07-03 19:25:42
【问题描述】:
我想知道为什么以下带有 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 数组的内存传输开销如此之大?
如果我从第一个 sn-p 替换该行
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 毫秒。 哇……哇哇……
【问题讨论】:
-
您在调试模式下得到正确的结果吗?随着存储到
tmp,编译器注意到结果没有被使用,甚至根本没有循环。此外,三个阵列中的每一个都是 256 MB,因此 RAM 带宽应该是决定因素。另请注意,Visual C++ 能够将普通算术表达式转换为 SSE 指令,尽管您似乎没有激活该选项。 -
您没有发布您测试的确切代码和数据,也没有指定您使用的核心,因此无法获得可比较的结果。在 Debug 构建中,for(;;) 循环的时间增加了一倍,这是意料之中的。在 Release 版本中,我看到 for(;;) 循环快了近三倍。看一下生成的机器代码就很清楚为什么:循环展开了,每次通过 8 次乘法。优化器无法对内在函数进行相同的优化。永远不要低估优化器的力量。
-
/arch:SSE2 是打开它的开关吗?但似乎 RAM 带宽确实可能是瓶颈,这意味着我无法使用 SSE 使其更快。
-
@Hans Passant:嗯,有趣的提示。我期待优化器也能处理 SSE。
-
一定要对内存带宽限制的言论加一点盐,你的测试证明它不是。如果是这样,展开循环永远不会有好处。这些天很难获得指令时序,但我确实在英特尔手册中看到 mulpd 具有 9 个周期的延迟和 9 个周期的 Atom 内核吞吐量。这有点多。
标签: c++ visual-c++ sse sse2