8

我有以下代碼,並期望使用exp()函數的內在版本。不幸的是,它不是在x64的構建,使得它比類似的Win32(即32位版本)慢:如何在x64代碼中獲得exp()函數的內部函數?

#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。然而,儘管我努力致電標準庫,但在x64構建中,exp()的執行速度較慢。

這裏是生成的彙編:

for (double i=0;i<NUM_ITERATIONS;++i) 
000000013F911030 movsd  xmm10,mmword ptr [[email protected] (13F912248h)] 
000000013F911039 movapd  xmm8,xmm6 
000000013F91103E movapd  xmm7,xmm9 
000000013F911043 movaps  xmmword ptr [rsp+20h],xmm11 
000000013F911049 movsd  xmm11,mmword ptr [[email protected] (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()功能。現在,讓我們來看看該for環與32位編譯生成的代碼:

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 [[email protected] (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 [[email protected] (102180h)] 
00101079 movsd  xmm2,mmword ptr [[email protected] (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 [[email protected] (102188h)] 
00101099 movsd  mmword ptr [esp+28h],xmm0 
0010109F movsd  mmword ptr [esp+30h],xmm1 
001010A5 ja   wmain+40h (101060h) 
    } 

太多的代碼存在,但它的速度更快。我一個3.3千兆赫的Nehalem-EP主機上做了一個定時試驗產生如下結果:

32位:

For loop body average exec time: 34.849229 cycles/10.560373 ns

64位:

For loop body average exec time: 45.845323 cycles/13.892522 ns

非常古怪的行爲,確實如此。爲什麼會發生?

更新:

我創建了一個Microsoft Connect bug report。歡迎自己動手,以獲得微軟本身對使用浮點內部函數的權威答案,特別是在x64代碼中。

+0

[本文](http://blogs.msdn.com/b/ricom/archive/2009/06/10/visual-studio-why-is-there-no-64-bit-version.aspx) (解釋爲什麼VS沒有64位版本)指出64位版本可能比32位版本慢。不過,我不知道這個解釋是否適用於你的具體情況。 – Attila 2012-04-10 20:01:02

+1

該文章是關於Visual Studio本身的64位版本,它與提出的問題無關。有很多因素可以使64位應用程序比32位應用程序慢。除非我錯過了一些東西,但是這些因素都與我關於浮點運算的問題沒有任何關係。 – 2012-04-10 20:04:10

+0

@MichaelGoldshteyn - 我的錯誤 – Attila 2012-04-10 20:08:28

回答

5

在x64,浮點運算使用SSE執行。這沒有exp()的內置操作,因此對標準庫的調用是不可避免的。我想你所指的MSDN文章是用使用8087 FP的32位代碼編寫的。

+0

請參閱我編輯的問題,其中包括由32位版本生成的代碼以及32位與64位的時序比較。這兩個版本都沒有使用「真實」內部函數,但是函數的調用有所不同,32位版本顯着更快。 – 2012-04-10 20:36:59

+0

也許,但事實上,在任何SSE操作碼中都沒有exp內部函數 – 2012-04-10 20:39:04

+0

這是真的,但我期待,根據MSDN文檔中exp()的內部實現在我的(彙編)代碼中內聯。 – 2012-04-10 20:40:44

0

我認爲微軟提供32位SSE2內部版本exp()的唯一原因是標準調用約定。 32位調用約定要求將操作數壓入主堆棧,並將結果返回到FPU堆棧的頂部寄存器中。如果您啓用了SSE2代碼生成功能,那麼返回值可能會從FPU堆棧彈出到內存中,然後從該位置加載到SSE2寄存器中,無論您想對結果執行什麼操作。顯然,將操作數傳遞到SSE2寄存器並將結果返回到SSE2寄存器會更快。這就是__libm_sse2_exp()所做的。在64位代碼中,標準調用約定傳遞操作數,並返回SSE2寄存器中的結果,因此在具有內部版本時沒有優勢。

32位SSE2和64位實現exp()之間性能差異的原因是Microsoft在兩種實現中使用了不同的算法。我不知道他們爲什麼要這樣做,並且他們爲一些操作數產生了不同的結果(不同的是1ulp)。

相關問題