【发布时间】:2012-04-23 03:21:19
【问题描述】:
我有以下代码,并期望使用 exp() 函数的内在版本。不幸的是,它不是 x64 版本,因此比类似的 Win32(即 32 位版本)要慢:
#include "stdafx.h"
#include <cmath>
#include <intrin.h>
#include <iostream>
int main()
{
const int NUM_ITERATIONS=10000000;
double expNum=0.00001;
double result=0.0;
for (double i=0;i<NUM_ITERATIONS;++i)
{
result+=exp(expNum); // <-- The code of interest is here
expNum+=0.00001;
}
// To prevent the above from getting optimized out...
std::cout << result << '\n';
}
我正在为我的构建使用以下开关:
/Zi /nologo /W3 /WX-
/Ox /Ob2 /Oi /Ot /Oy /GL /D "WIN32" /D "NDEBUG"
/D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm-
/EHsc /GS /Gy /arch:SSE2 /fp:fast /Zc:wchar_t /Zc:forScope
/Yu"StdAfx.h" /Fp"x64\Release\exp.pch" /FAcs /Fa"x64\Release\"
/Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gd /errorReport:queue
如您所见,根据MSDN article on intrinsics 的要求,我确实有/Oi、/O2 和/fp:fast。然而,尽管我做出了努力,还是调用了标准库,这使得 exp() 在 x64 构建上的执行速度变慢了。
这是生成的程序集:
for (double i=0;i<NUM_ITERATIONS;++i)
000000013F911030 movsd xmm10,mmword ptr [__real@3ff0000000000000 (13F912248h)]
000000013F911039 movapd xmm8,xmm6
000000013F91103E movapd xmm7,xmm9
000000013F911043 movaps xmmword ptr [rsp+20h],xmm11
000000013F911049 movsd xmm11,mmword ptr [__real@416312d000000000 (13F912240h)]
{
result+=exp(expNum);
000000013F911052 movapd xmm0,xmm7
000000013F911056 call exp (13F911A98h) // ***** exp lib call is here *****
000000013F91105B addsd xmm8,xmm10
expNum+=0.00001;
000000013F911060 addsd xmm7,xmm9
000000013F911065 comisd xmm8,xmm11
000000013F91106A addsd xmm6,xmm0
000000013F91106E jb main+52h (13F911052h)
}
正如您在上面的程序集中看到的,有一个对exp() 函数的调用。现在,让我们看看使用 32 位构建为 for 循环生成的代码:
for (double i=0;i<NUM_ITERATIONS;++i)
00101031 xorps xmm1,xmm1
00101034 rdtsc
00101036 push ebx
00101037 push esi
00101038 movsd mmword ptr [esp+1Ch],xmm0
0010103E movsd xmm0,mmword ptr [__real@3ee4f8b588e368f1 (102188h)]
00101046 push edi
00101047 mov ebx,eax
00101049 mov dword ptr [esp+3Ch],edx
0010104D movsd mmword ptr [esp+28h],xmm0
00101053 movsd mmword ptr [esp+30h],xmm1
00101059 lea esp,[esp]
{
result+=exp(expNum);
00101060 call __libm_sse2_exp (101EC0h) // <--- Quite different from 64-bit
00101065 addsd xmm0,mmword ptr [esp+20h]
0010106B movsd xmm1,mmword ptr [esp+30h]
00101071 addsd xmm1,mmword ptr [__real@3ff0000000000000 (102180h)]
00101079 movsd xmm2,mmword ptr [__real@416312d000000000 (102178h)]
00101081 comisd xmm2,xmm1
00101085 movsd mmword ptr [esp+20h],xmm0
expNum+=0.00001;
0010108B movsd xmm0,mmword ptr [esp+28h]
00101091 addsd xmm0,mmword ptr [__real@3ee4f8b588e368f1 (102188h)]
00101099 movsd mmword ptr [esp+28h],xmm0
0010109F movsd mmword ptr [esp+30h],xmm1
001010A5 ja wmain+40h (101060h)
}
那里的代码更多,但速度更快。我在 3.3 GHz Nehalem-EP 主机上进行的时序测试产生了以下结果:
32 位:
对于循环体平均执行时间:34.849229 个周期 / 10.560373 ns
64 位:
对于循环体平均执行时间:45.845323 个周期 / 13.892522 ns
确实是非常奇怪的行为。为什么会这样?
更新:
我创建了一个Microsoft Connect bug report。请随意投票,以获得 Microsoft 本身关于浮点内在函数使用的权威答案,尤其是在 x64 代码中。
【问题讨论】:
-
This article(解释为什么 VS 没有 64 位版本)指出 64 位版本可能比 32 位版本慢。不过,我不知道这种解释是否适用于您的具体情况。
-
那篇文章是关于 64 位版本的 Visual Studio 本身,它与提出的问题无关。有许多因素可以使 64 位应用程序比 32 位应用程序慢。但是,除非我遗漏了什么,否则这些因素都与我关于浮点计算的问题无关。
-
@MichaelGoldshteyn - 我的错误
-
GregC,删除 /D "WIN32" 对生成的代码没有影响。
-
@GregC,关于您指向 software.intel.com 的链接...,我们没有在我们的项目中使用 SVML 库,所以我没有。我只是想让构建不辜负微软基于 MSDN 的“保证”。
标签: c++ visual-studio-2010 visual-c++ visual-c++-2010 intrinsics