Tal vez recordéis Sieve en PowerBASIC Console Compiler 6 donde comparaba la última versión de PowerBasic Console Compiler (6.04) de 2011, con la anterior (5.0) de 2008.

Continuando con el artículo de WinAPI contra C, intrínsecas, C++ y ensamblador donde evaluábamos diferentes mecanismos de hacer lo mismo sobre Windows, hoy compararemos la evolución que ha tenido Microsoft Visual C++ en estos 11 años.

Pues aprovechando que me he encontrado de casualidad con los ejecutables de Visual C++ 2008 que publiqué en x86 vs x64 he procedido a comparar el rendimiento con el último Visual C++ 2017. Me hubiera gustado hacerlo sobre Sieve en C, pero creo que este ejemplo mucho más sencillo y en C portable será más claro.

El código fuente original, era bien sencillo, un bucle que se ejecuta 10.000.000.000 veces y realiza una suma y un decremento:

#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 resultados han sido magníficos:

CompiladorTamaño ejecutable (bytes)Tiempo de ejecución (ms)
Visual C++ 2008 (9.0)66.0487.531
Visual C++ 2017 (14.14)116.7364.984



La velocidad del ejecución del código generado con Visual C++ 14.1 (2017) casi dobla a la del que generaba 11 años antes. Desgraciadamente también el tamaño del ejecutable ha crecido en la misma proporción. Obviamente el auge x64 es en donde mayores mejoras vamos a notar, pero no deja de ser curioso que el mismo código que escribiéramos hace 10 años, pueda correr el doble de rápido con sólo recompilarlo.

Analicemos un poco más en detalle lo que ha ocurrido. Visual C++ 2008 lo convertía al siguiente código ensamblador:

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]

Y en Visual C++ 2017 es así:

xor	ebx, ebx
mov	rax, 10000000000
mov	ecx, 1
$[email protected]:
dec	rbx
add	rbx, rcx
inc	rcx
cmp	rcx, rax
jbe	SHORT $[email protected]

Viéndolo así, se entiende fácilmente. Porque Visual C++ 2017 es mucho más listo y evita el uso de LEA para realizar el cálculo. Aunque es eficiente, es poco paralelizable en la pipeline, en donde triunfan las instrucciones sencillas tipo RISC.

Como siempre hago, puedes descargarte el código fuente y los ejecutables aquí (90 Kb. en formato ZIP).