Estaba cotilleando las novedades de GCC, y me he encontrado con lo que llaman __builtin_expect. Esta macro, permite indicar al compilador la probabilidad de una condición, de manera que el predictor de saltos sea capaz de generar un código más eficiente.

Lo habitual es que se usen con una macro más legible llamada likely / unlikely, y que indica una probabilidad alta, y una baja respectivamente.

Imaginemos un fragmento de código que lee un pixel, y en el caso de ser de color negro, retorna cierto, y falso en caso contrario:

if (GetPixel() == BLACK)
{
	return(true);
}
else
{
	return(false);
}

Digamos que el compilador podría traducir este código a x86 de una forma parecida a esta:

call	GetPixel
cmp	eax, BLACK
je	true
false:
	xor	eax, eax
	ret
true:
	mov	eax, 1
	ret

De este modo nos encontramos que a nivel de lenguaje máquina, cuando el píxel sea de color negro, entonces se ejecutará el salto a la etiqueta true: y retornará cierto. Si no lo es, no se ejecuta el salto, y retorna falso.

Si sabemos que lo habitual es que los pixels sean de color negro, estamos haciendo que el microprocesador deba ejecutar casi siempre el salto, esto es bastante más lento, y exige un esfuerzo adicional al predictor de saltos. Así que si usamos GCC, lo puede tener en cuenta, y generar un código más eficiente.

if (likely(GetPixel() == BLACK))
{
	return(true);
}
else
{
	return(false);
}

Que lo que generará será de este estilo:

call	GetPixel
cmp	eax, BLACK
jne	false
true:
	mov	eax, 1
	ret
false:
	xor	eax, eax
	ret

Ya lo tenemos, y ahora la mayoría de casos, es decir, píxels negros se ejecutarán de manera más eficaz. No sería una mejora notable, pero entendida por ejemplo en el contexto de un bucle que se repite más de 2 millones de veces para todos los pixels de la pantalla, puede marcar la diferencia.

Desde luego que yo no habría escrito ese código como un if, y probablemente vosotros tampoco, de todas formas likely y unlikely, puede ser aplicado también en esos casos:

return(likely(GetPixel() == BLACK));

En muchos casos, el optimizador es capaz de detectar automáticamente estos casos, y así generar el mejor código posible sin necesidad de usar likely/unlikely. Bien sea por el análisis estático del código en casos sencillos, bien sea usando PGO. Sin embargo en otros, será incapaz de hacerlo sin nuestra ayuda. Es por eso que el kernel de Linux, utiliza ya estas macros, para así conseguir mejores rendimientos.

Quizás en el futuro, este soporte se extienda a otros compiladores distintos de GCC / G++, pero de hasta que eso no ocurra, lo mejor es aplicar unos define de preprocesador para mantener la portabilidad:

#ifdef __GNUC__
	#define likely(x)	__builtin_expect(!!(x), 1)
	#define unlikely(x)	__builtin_expect(!!(x), 0)
#else
	#define likely(x)	(x)
	#define unlikely(x)	(x)
#endif