Para comprobar la ganancia que se podría obtener compilando para Windows 64 bits las aplicaciociones de 32 bits, he hecho un pequeño ejemplo que utiliza aritmética de 64 bits, y que pueda ser utilizado como benchmark comparativo.

Intentando que sea lo más real posible, he configurado el Visual C++ 2005 SP1 para que utilice en x86 instrucciones SSE2 (/arch:SSE2), y así poder manipular la información en bloques de más de 32 bits, aunque como se verá más adelante, el compilador no ha considerado que se obtenga ningún beneficio usándolas.

El programa utilizado, simplemente realiza un bucle de 10 millardos de iteraciones, donde se ejecuta una suma y un decremento. Al finalizar, se muestra en la consola el tiempo invertido.

#include <stdio.h>
#include <time.h>
void __cdecl main (void)
{
unsigned __int64 iCount, iRes;
unsigned int iInicio, iFin;
iInicio=clock();
iRes=0;
for (iCount=1; iCount<=10000000000; iCount++)
{
iRes+=iCount;
iRes--;
}
iFin=clock();
printf("%I64d %d\n", iRes, iFin-iInicio);
getchar();
}

Los tiempos obtenidos han sido:

Arquitectura Tiempo (milisegundos): Relativo:
x86 41404 100%
x64 20015 206%

Los números cantan, la mejora de rendimiento cuando se usan números de 64 bits, es tremenda, superior al doble.

Pasemos a analizar con algo más de detalle la clave del programa: el bucle principal en C:

iRes=0;
for (iCount=1; iCount<=10000000000; iCount++)
{
iRes+=iCount;
iRes--;
}

Como se puede ver, para cada iteración se debe realizar una comparación de un número de 64 bits con un inmediato de 64 bits (iCount<=10000000000); un incremento de un número de 64 bits (iCount++); una adición de dos números de 64 bits (iRes+=iCount); y por último un decremento en uno de otro número de 64 bits (iRes–). En total 4 operaciones sobre valores de 64 bits.

Una vez compilado el código, el bucle anterior queda traducido en ensamblador x86 así:

xor esi, esi
xor edi, edi
mov ecx, 1
xor eax, eax
[email protected]:
mov edx, ecx
add edx, -1
mov ebp, eax
adc ebp, -1
add esi, edx
adc edi, ebp
add ecx, 1
adc eax, 0
cmp eax, 2
jb SHORT [email protected]
ja SHORT [email protected]
cmp ecx, 1410065408
jbe SHORT [email protected]
[email protected]:

Es decir, requiere por cada iteración la ejecución de entre 9 y 13 instrucciones. Por otro lado, el mismo código en versión x64 es:

xor ebx, ebx
mov ecx, 1
mov edi, eax
mov rax, 10000000000
[email protected]:
lea rbx, QWORD PTR [rbx+rcx-1]
inc rcx
cmp rcx, rax
jbe SHORT [email protected]

Es decir solamente 4 instrucciones por iteración. Una mejora evidente es que el código actual es single-threaded, por lo que no se beneficia en absoluto de los procesadores multicore, ni del hyperthreading. Como el algoritmo es puramente sintético se podría repartir la carga por partes iguales a cada core, obteniéndose algo muy cercano al 200% de mejora adicional en equipos dual core.

En éste paquete (62 Kb. en formato ZIP), se incluyen el fuente, los binarios x86 y x64, el proyecto, y los listados en ensamblador de ambas versiones.