La mayor parte del código de alto rendimiento en los juegos de consola modernos se escribe utilizando una especie de punto medio entre el ensamblaje y C ++: intrínsecos del compilador . Estas construcciones se ven y analizan como funciones de C ++, pero en realidad se traducen en instrucciones de máquina única . Entonces, por ejemplo, mi "sujetar cada valor del vector V para que sea> = a y <= b" se ve como
// for each v.x, ensure v.x >= a.x && v.x <= b.x
inline __m128 ClampSIMD( const __m128 &v, const __m128 & a, const __m128 & b )
{
return _mm_max_ps( a, _mm_min_ps( v, b ) );
}
En funciones como estas, sigo pensando en términos de las instrucciones específicas de la máquina. , pero tengo la conveniencia de escribirlas en C para no tener que preocuparme por registrar el color y la programación, cargar operaciones y otros detalles aburridos.
Todavía necesita saber qué instrucciones admite la CPU, especialmente porque los compiladores modernos son terribles para vectorizar código, en comparación con lo bien que un humano inteligente puede hacer el trabajo. También, a veces, los detalles sutiles de cómo organizar su código pueden tener enormes implicaciones para el rendimiento que no son obvias sin comprender lo que está haciendo la máquina.
Aunque es posible que no codifiquemos en el ensamblaje, todavía depuramos mucho en el ensamblado. La optimización de los compiladores reorganiza agresivamente el código de manera que los depuradores no puedan seguir el ritmo, por lo que a menudo, al depurar un "modo de lanzamiento", lo mejor es abrir el desensamblador y rastrear el código de esa manera. Esta charla de GDC sobre "Depuración forense" de bloqueos ilustra muchos de los por qué y cómo de la depuración a ese nivel.