c++ - online - compilar y ejecutar c en windows
¿Por qué el compilador en línea produce un código más lento que la creación manual? (2)
Respuesta corta:
Su matriz asd
se declara así:
int *asd=new int[16];
Por lo tanto, use int
como el tipo de retorno en lugar de bool.
Alternativamente, cambie el tipo de matriz a bool
.
En cualquier caso, haga que el tipo de retorno de la función de test
coincida con el tipo de la matriz.
Pase al pie de página para más detalles.
Respuesta larga:
En la versión insertada manualmente, el "núcleo" de una iteración se ve así:
xor eax,eax
mov edx,ecx
and edx,0Fh
mov dword ptr [ebp+edx*4],eax
mov eax,dword ptr [esp+1Ch]
movss xmm0,dword ptr [eax]
movss xmm1,dword ptr [edi]
cvtps2pd xmm0,xmm0
cvtps2pd xmm1,xmm1
comisd xmm1,xmm0
La versión impresa del compilador es completamente idéntica, excepto por la primera instrucción.
Donde en lugar de:
xor eax,eax
Tiene:
xor eax,eax
movzx edx,al
De acuerdo, entonces es una instrucción adicional. Ambos hacen lo mismo: poner a cero un registro. Esta es la única diferencia que veo ...
La instrucción movzx
tiene una latencia de ciclo único y rendimiento recíproco de ciclo de 0.33
en todas las arquitecturas más nuevas. Entonces no puedo imaginar cómo esto podría hacer una diferencia del 10%.
En ambos casos, el resultado de la puesta a cero se usa solo 3 instrucciones más adelante. Entonces, es muy posible que esto pueda estar en la ruta crítica de la ejecución.
Si bien no soy un ingeniero de Intel, esta es mi suposición:
La mayoría de los procesadores modernos se ocupan de las operaciones de reducción a cero (como xor eax,eax
) mediante el cambio de nombre de registro a un banco de cero registros. Desvía por completo las unidades de ejecución. Sin embargo, es posible que este manejo especial pueda causar una burbuja en la tubería cuando se accede al registro (parcial) a través de movzx edi,al
.
Además, también existe una falsa dependencia de eax
en la versión en línea del compilador:
movzx edx,al
mov eax,ecx // False dependency on "eax".
Si la ejecución fuera de orden es capaz o no de resolver esto, me supera.
De acuerdo, esto se está convirtiendo básicamente en una cuestión de ingeniería inversa del compilador de MSVC ...
Aquí voy a explicar por qué se genera tanto movzx
adicional como por qué se mantiene.
La clave aquí es el valor de retorno bool
. Aparentemente, los tipos de datos bool
son probablemente como valores almacenados de 8 bits dentro de la representación interna de MSVC. Por lo tanto, cuando conviertes implícitamente de bool
a int
aquí:
asd[j%16] = a.test(b);
^^^^^^^^^ ^^^^^^^^^
type int type bool
hay una promoción de entero de 8 bits -> 32 bits. Esta es la razón por la cual MSVC genera la instrucción movzx
.
Cuando la alineación se realiza manualmente, el compilador tiene suficiente información para optimizar esta conversión y mantiene todo como un IR de tipo de datos de 32 bits.
Sin embargo, cuando el código se coloca en su propia función con un valor de retorno bool
, el compilador no puede optimizar el tipo de datos intermedio de 8 bits. Por lo tanto, el movzx
queda.
Cuando haces que ambos tipos de datos sean iguales (ya sea int
o bool
), no se necesita conversión. Por lo tanto, el problema se evita por completo.
Fondo
El siguiente bucle crítico de una pieza de software numérico, escrito en C ++, básicamente compara dos objetos por uno de sus miembros:
for(int j=n;--j>0;)
asd[j%16]=a.e<b.e;
b
son de clase ASD
:
struct ASD {
float e;
...
};
Estaba investigando el efecto de poner esta comparación en una función miembro liviana:
bool test(const ASD& y)const {
return e<y.e;
}
y usándolo así:
for(int j=n;--j>0;)
asd[j%16]=a.test(b);
El compilador está haciendo hincapié en esta función, pero el problema es que el código de ensamblado será diferente y causará> 10% de sobrecarga de tiempo de ejecución. Tengo que preguntar:
Preguntas
¿Por qué el compilador produce diferentes códigos de ensamblado?
¿Por qué el montaje producido es más lento?
EDIT: la segunda pregunta ha sido respondida mediante la implementación de la sugerencia de @ KamyarSouri (j% 16). El código de ensamblado ahora se ve casi idéntico (vea http://pastebin.com/diff.php?i=yqXedtPm ). Las únicas diferencias son las líneas 18, 33, 48:
000646F9 movzx edx,dl
Material
- El código de prueba: http://pastebin.com/03s3Kvry
- La salida de ensamblaje en MSVC10 con / Ox / Ob2 / Ot / arch: SSE2:
- Versión en inglés del compilador: http://pastebin.com/yqXedtPm
- Versión en línea manual: http://pastebin.com/pYSXL77f
- Diferencia http://pastebin.com/diff.php?i=yqXedtPm
Esta tabla muestra los FLOP / s (hasta un factor de escala) durante 50 pruebas de mi código.
La secuencia de comandos gnuplot para generar la trama: http://pastebin.com/8amNqya7
Opciones del compilador:
/ Zi / W3 / WX- / MP / Ox / Ob2 / Oi / Ot / Oy / GL / D "WIN32" / D "NDEBUG" / D "_CONSOLE" / D "_UNICODE" / D "UNICODE" / Gm- / EHsc / MT / GS- / Gy / arch: SSE2 / fp: precise / Zc: wchar_t / Zc: forScope / Gd / analyse-
Opciones del enlazador: / INCREMENTAL: NO "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32. lib "" uuid.lib "" odbc32.lib "" odbccp32.lib "/ ALLOWISOLATION / MANIFESTUAC:" level = ''asInvoker'' uiAccess = ''false'' "/ SUBSYSTEM: CONSOLE / OPT: REF / OPT: ICF / LTCG / TLBID : 1 / DYNAMICBASE / NXCOMPAT / MACHINE: X86 / ERRORREPORT: QUEUE
lea esp,[esp]
ocupa 7 bytes de i-cache y está dentro del ciclo. Algunas otras pistas hacen que parezca que el compilador no está seguro de si se trata de una versión de lanzamiento o una versión de depuración.
Editar:
The lea esp,[esp]
no está en el circuito. La posición entre las instrucciones de los alrededores me confundió. Ahora parece que se desperdició intencionalmente 7 bytes, seguido de otro desperdicio de 2 bytes, para iniciar el bucle real en un límite de 16 bytes. Lo que significa que esto realmente acelera las cosas, como lo observó Johennes Gerer.
Sin embargo, el compilador aún no está seguro de si esto es una versión de depuración o versión.
Otra edición:
El diff pastabin es diferente del diff pastebin que vi antes. Esta respuesta podría eliminarse ahora, pero ya tiene comentarios, así que lo dejo.