¿Cómo puedo obtener un intrínseco para la función exp () en código x64?
Tengo el siguiente código y estoy esperando la versión intrínseca delexp()
Función a utilizar. Desafortunadamente, no está en una compilación x64, por lo que es más lento que un Win32 similar (es decir, una compilación de 32 bits):
#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';
}
Estoy usando los siguientes interruptores para mi compilació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
Como puedes ver, tengo/Oi
, /O2
y/fp:fast
según lo requerido por elArtículo de MSDN sobre intrínsecos.. Sin embargo, a pesar de mis esfuerzos, se hace un llamado a la biblioteca estándar,exp()
Realiza más lento en las versiones x64.
Aquí está el ensamblado generado:
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)
}
Como puede ver en el conjunto anterior, hay una llamada a laexp()
función. Ahora, veamos el código generado para eso.for
bucle con una construcción de 32 bits:
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)
}
Hay mucho más código allí, pero es más rápido. Una prueba de tiempo que realicé en un host Nehalem-EP de 3,3 GHz produjo los siguientes resultados:
32 bits:
Para el tiempo de ejecución promedio del cuerpo del bucle: 34.849229 ciclos / 10.560373 ns
64 bits:
Para el tiempo de ejecución promedio del cuerpo del bucle: 45.845323 ciclos / 13.892522 ns
Muy extraño comportamiento, por cierto. ¿Por qué está sucediendo?
Actualizar:
He creado unInforme de error de Microsoft Connect. No dude en solicitarlo para obtener una respuesta autorizada del propio Microsoft sobre el uso de intrínsecos de punto flotante, especialmente en el código x64.