5

EDITVista de desmontaje del código de liberación de C# 64-bit es un 75% más largo que el código de depuración de 32 bits?

He probado la versión en 32 bits, y el código era compacto. Por lo tanto, la siguiente es una cuestión de 64 bits.


Estoy usando VS 2012 RC. Debug es de 32 bits, y Release es de 64 bits. A continuación se muestra la depuración a continuación, suelte el desmontaje de una línea de código:

  crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
0000006f mov   eax,dword ptr [ebp-40h] 
00000072 shr   eax,8 
00000075 mov   edx,dword ptr [ebp-3Ch] 
00000078 mov   ecx,0FF00h 
0000007d and   edx,ecx 
0000007f shr   edx,8 
00000082 mov   ecx,dword ptr [ebp-40h] 
00000085 mov   ebx,0FFh 
0000008a and   ecx,ebx 
0000008c xor   edx,ecx 
0000008e mov   ecx,dword ptr ds:[03387F38h] 
00000094 cmp   edx,dword ptr [ecx+4] 
00000097 jb   0000009E 
00000099 call  6F54F5EC 
0000009e xor   eax,dword ptr [ecx+edx*4+8] 
000000a2 mov   dword ptr [ebp-40h],eax 
----------------------------------------------------------------------------- 
     crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
000000a5 mov   eax,dword ptr [rsp+20h] 
000000a9 shr   eax,8 
000000ac mov   dword ptr [rsp+38h],eax 
000000b0 mov   rdx,124DEE68h 
000000ba mov   rdx,qword ptr [rdx] 
000000bd mov   eax,dword ptr [rsp+00000090h] 
000000c4 and   eax,0FF00h 
000000c9 shr   eax,8 
000000cc mov   ecx,dword ptr [rsp+20h] 
000000d0 and   ecx,0FFh 
000000d6 xor   eax,ecx 
000000d8 mov   ecx,eax 
000000da mov   qword ptr [rsp+40h],rdx 
000000df mov   rax,qword ptr [rsp+40h] 
000000e4 mov   rax,qword ptr [rax+8] 
000000e8 mov   qword ptr [rsp+48h],rcx 
000000ed cmp   qword ptr [rsp+48h],rax 
000000f2 jae   0000000000000100 
000000f4 mov   rax,qword ptr [rsp+48h] 
000000f9 mov   qword ptr [rsp+48h],rax 
000000fe jmp   0000000000000105 
00000100 call  000000005FA5D364 
00000105 mov   rax,qword ptr [rsp+40h] 
0000010a mov   rcx,qword ptr [rsp+48h] 
0000010f mov   ecx,dword ptr [rax+rcx*4+10h] 
00000113 mov   eax,dword ptr [rsp+38h] 
00000117 xor   eax,ecx 
00000119 mov   dword ptr [rsp+20h],eax 

¿Qué es todo el código extra en la versión de 64 bits haciendo? Está probando para qué? No hice una evaluación comparativa de esto, pero el código de 32 bits debería ejecutarse mucho más rápido.

EDITAR

Toda la función:

public static uint CRC32(uint val) 
{ 
    uint crc = 0xffffffff; 

    crc = (crc >> 8)^crcTable[(val & 0x000000ff)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[((val & 0x00ff0000) >> 16)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[(val >> 24)^crc & 0xff]; 

    // flip bits 
    return (crc^0xffffffff); 
} 
+0

Ayudaría a ver toda la función, no solo una línea de la misma. –

+3

Tratando de comparar las diferencias cuando una está construida para la depuración y la otra versión no es válida. Compare al menos la misma especie (depuración/depuración o liberación/liberación). –

+0

@KenWhite Estoy de acuerdo. Pero es el hecho de que la depuración es más compacta que me llamó la atención, más el hecho de liberar verificaciones adicionales. – IamIC

Respuesta

8

Sospecho que está utilizando "Ir al desmontaje" al depurar la versión de lanzamiento para obtener el código de ensamblado.

Después de ir a Herramientas -> Opciones, Depuración, General, y deshabilitar "Suprimir la optimización de JIT en la carga del módulo" Obtuve una lista de ensamblaje x64 sin comprobación de errores.

Parece que, por defecto, incluso en el modo de lanzamiento, el código no está optimizado si el depurador está conectado. Téngalo en cuenta cuando intente comparar su código.

PD: La evaluación comparativa muestra x64 ligeramente más rápido que x86, 4.3 vs 4.8 segundos para llamadas de funciones de mil millones.

Editar: Los puntos de interrupción todavía me funcionaron, de lo contrario no habría podido ver el desmontaje después de desmarcarlo.Su línea de ejemplo de arriba se parece a esto (VS 2012 RC):

crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
00000030 mov   r11d,eax 
00000033 shr   r11d,8 
00000037 mov   ecx,edx 
00000039 and   ecx,0FF00h 
0000003f shr   ecx,8 
00000042 movzx  eax,al 
00000045 xor   ecx,eax 
00000047 mov   eax,ecx 
00000049 cmp   rax,r9 
0000004c jae   00000000000000A4 
0000004e mov   eax,dword ptr [r8+rax*4+10h] 
00000053 xor   r11d,eax 
+1

Sí, estás en lo cierto. Por lo tanto, parece que en el modo de depuración de 64 bits, las comprobaciones se agregan, pero no en el modo de 32 bits. Por lo tanto, la versión real será "limpia". – IamIC

+0

Naturalmente, con esa opción desactivada, "Ir al desmontaje" y los puntos de interrupción dejan de funcionar, por lo que no puedo verificar. Pero creo que usted respondió la pregunta, respaldado por sus puntos de referencia. – IamIC

+0

No tengo idea de cómo está consiguiendo que funcionen los puntos de interrupción y puedo ver el desmontaje. ¿Estás ejecutando la versión de 64 bits? ¿Qué VS? – IamIC

1

Mirando el código de esto está relacionado con la comprobación de errores para acceder a crcTable. Está haciendo tus límites antes de que empiece a cavar en la matriz.

En el código de 32 bits que ver este

0000008e mov   ecx,dword ptr ds:[03387F38h] 
.... 
0000009e xor   eax,dword ptr [ecx+edx*4+8] 

En este caso se trata de la carga de la dirección base de la matriz de 03387F38h y luego utilizando la aritmética de punteros estándar para acceder a la entrada correcta.

En el código de 64 bits esto parece ser más complicado.

000000b0 mov   rdx,124DEE68h 
000000ba mov   rdx,qword ptr [rdx] 

Esto carga una dirección en el RDX registro

000000da mov   qword ptr [rsp+40h],rdx 
... 
00000105 mov   rax,qword ptr [rsp+40h] 
0000010a mov   rcx,qword ptr [rsp+48h] 
0000010f mov   ecx,dword ptr [rax+rcx*4+10h] 

Esto mueve la dirección en la pila, luego más tarde se mueve en el registro rax y hace el mismo trabajo puntero para acceder a la matriz .

Casi todo entre 000000da y 00000100/00000105 parece ser código de validación. El resto del código se correlaciona bastante bien entre el código de 64 bits y el de 32 bits, con una utilización del registro menos agresiva en el código de 64 bits.

+0

Hice una edición: probé el modo de lanzamiento en 32 bits, y la salida fue idéntica a la depuración de 32 bits: corta. Este es ciertamente un problema de 64 bits. – IamIC

+0

@IanC Ah, es bueno saberlo. Actualicé la respuesta para tener eso en cuenta. –

+0

Esto es realmente inesperado. Construir en el modo de 32 bits dará como resultado un código más rápido. Ahora me pregunto si el código de 64 bits está plagado de verificación de límites de matriz y código de acceso complejo. – IamIC

0

exp^CRC & 0xff se compila como exp^(cr & 0xff):

00000082 mov   ecx,dword ptr [ebp-40h] 
00000085 mov   ebx,0FFh 
0000008a and   ecx,ebx 
0000008c xor   edx,ecx 

hay que escribir la expresión como ?

(exp^crc) & 0xff 

La versión de 64 bits está definitivamente menos optimizada que la versión de 32 bits. CLR tiene dos implementaciones de compilador JIT separadas.

Además, si el rendimiento es crítico, use un código no seguro para eliminar los límites.

+0

Aparentemente, la versión completa en 64 bits es un 12% más rápida que la de 32 bits. La respuesta es que el depurador adjunto evitó las optimizaciones en 64 bits, pero no en 32. De la forma en que está configurado. El código es CRC estándar. – IamIC

Cuestiones relacionadas