Как я могу получить встроенную функцию exp () в коде x64?
У меня есть следующий код, и я ожидаю внутреннюю версиюexp()
функция, которая будет использоваться. К сожалению, его нет в сборке x64, что делает его медленнее, чем аналогичный Win32 (то есть 32-разрядная сборка):
<code>#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'; } </code>
Я использую следующие ключи для моей сборки:
<code>/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 </code>
Как видите, у меня есть/Oi
, /O2
а также/fp:fast
как требуется согласноСтатья MSDN о внутренностях, Тем не менее, несмотря на мои усилия, сделан вызов стандартной библиотеке, что делаетexp()
работать медленнее на сборках x64.
Вот сгенерированная сборка:
<code> 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) } </code>
Как вы можете видеть в сборке выше, есть призыв кexp()
функция. Теперь давайте посмотрим на код, сгенерированный для этогоfor
цикл с 32-битной сборкой:
<code> 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) } </code>
Гораздо больше кода, но это быстрее. Временной тест, который я провел на хосте Nehalem-EP с тактовой частотой 3,3 ГГц, дал следующие результаты:
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
Действительно, очень странное поведение. Почему это происходит?
Update:
Я создалОтчет об ошибке Microsoft Connect, Не стесняйтесь высказать свое мнение, чтобы получить авторитетный ответ от самой Microsoft об использовании встроенных функций с плавающей точкой, особенно в коде x64.