C++ 如何在x64代码中获取exp()函数的内部函数?

C++ 如何在x64代码中获取exp()函数的内部函数?,c++,visual-studio-2010,visual-c++,visual-c++-2010,intrinsics,C++,Visual Studio 2010,Visual C++,Visual C++ 2010,Intrinsics,我有以下代码,希望使用exp()函数的内部版本。不幸的是,它不在x64版本中,这使得它比类似的Win32(即32位版本)慢: 如您所见,我确实按照要求提供了/Oi、/O2和/fp:fast。然而,尽管我做出了努力,还是调用了标准库,使得exp()在x64版本上的执行速度变慢 以下是生成的程序集: for (double i=0;i<NUM_ITERATIONS;++i) 000000013F911030 movsd xmm10,mmword ptr [__real@3ff0

我有以下代码,希望使用
exp()
函数的内部版本。不幸的是,它不在x64版本中,这使得它比类似的Win32(即32位版本)慢:

如您所见,我确实按照要求提供了
/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)  
  }
代码多得多,但速度更快。我在3.3 GHz Nehalem EP主机上进行的定时测试产生了以下结果:

32位:

循环体平均执行时间:34.849229个周期/10.560373纳秒

64位:

循环体平均执行时间:45.845323个周期/13.892522纳秒

确实是非常奇怪的行为。为什么会这样

更新:

我创造了一个新的世界。请随意投票,从微软自己那里获得关于浮点内部函数使用的权威答案,特别是在x64代码中。

编辑我想在本次讨论中添加到和的链接

在初始检查时,应该有一种方法使用F2XM1来计算指数。但是,它在x87指令集中


明确使用MMX/x87是有希望的,正如在x64上发布的文章中所述,浮点运算是使用SSE执行的。这没有用于
exp()
的内置操作,因此,除非您编写自己的内联手动矢量化
\uuum128d exp(\uuuuum128d)
(),否则调用标准库是不可避免的


我想您提到的MSDN文章是用32位代码编写的,其中使用了8087 FP。

我认为Microsoft提供32位SSE2 exp()固有版本的唯一原因是标准调用约定。32位调用约定要求将操作数推送到主堆栈上,并在FPU堆栈的顶部寄存器中返回结果。如果启用了SSE2代码生成,则返回值可能会从FPU堆栈弹出到内存中,然后从该位置加载到SSE2寄存器中,以便对结果执行任何数学运算。显然,在SSE2寄存器中传递操作数并在SSE2寄存器中返回结果更快。这就是uu libm_sse2_exp()所做的。在64位代码中,标准调用约定传递操作数并返回SSE2寄存器中的结果,因此具有内部版本没有任何优势


exp()的32位SSE2和64位实现之间性能差异的原因是Microsoft在这两种实现中使用了不同的算法。我不知道它们为什么会这样做,它们会对某些操作数产生不同的结果(不同1ulp)。

(解释为什么VS没有64位版本)指出,64位构建可能比32位构建慢。不过,我不知道这种解释是否适用于您的具体案例。那篇文章是关于Visual Studio本身的64位版本,与提出的问题无关。有许多因素会使64位应用程序比32位应用程序慢。除非我遗漏了什么,否则这些因素都与我关于浮点计算的问题无关。@MichaelGoldshteyn-我的错误GregC,删除/D“WIN32”对生成的代码没有影响。@GregC,关于你到software.intel.com的链接,我们在项目中没有使用SVML库,所以没有。我只是想让构建符合微软基于MSDN的“保证”。请参阅我编辑的问题,其中包括由32位构建生成的代码以及32位与64位的定时比较。这两个构建都没有使用“true”内在函数,但调用的函数之间存在差异,32位构建速度明显更快。可能吧,但事实是,在任何SSE操作码中都没有exp内在函数。这是真的,但根据MSDN文档,我期待exp()的内在实现在我的(汇编)代码中内联。我打赌文档根本没有更新到SSE codegen。我怀疑如果从选项中删除/arch:sse2并以8087 FPU为目标,那么您将看到正在进行的内部调用。从32位构建中删除sse2会产生完全不同的代码,它使用8087“f”指令,我看不到任何
exp()
lib调用。不过,代码的速度几乎慢了三倍。然而,你似乎确实了解了一些事情。对于64位版本,不可能在编译器中禁用SSE2的使用,因为所有64位处理器都必须支持它。因此,生成的(汇编)代码没有变化。
  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)  
  }
  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)  
  }