¿Por qué los motores necesitan ser optimizados para nuevos procesadores de la misma arquitectura?

39

Cuando se lanza una nueva generación de procesadores, la mayoría de los sitios web informan que los motores y programas de juegos deben optimizarse para el nuevo hardware. No entiendo bien por qué. Un procesador generalmente tiene una arquitectura que define qué tipo de conjunto de instrucciones utiliza. El que todos usamos hoy en día es amd_x86_64. ¿Por qué debería actualizarse cualquier programa o compilador si todos los procesadores usan esta misma arquitectura? Seguramente hay características DENTRO de la tubería del nuevo procesador que optimiza la ejecución del código de la máquina, pero ¿por qué habría que cambiar el código de la máquina si la arquitectura no lo hizo?

salbeira
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Josh
14
"Necesidad" es una redacción incorrecta, y más marketing que verdad, de la misma manera que, por ejemplo, Windows necesita admitir una nueva generación de CPU (o no, como en el caso de Windows 7, que en principio funcionaría perfectamente) bien con, por ejemplo, Ryzen, excepto para usar 3-4% más potencia de la necesaria). Este ajuste se trata solo de tratar de exprimir un poco más de la CPU, acercándose al máximo. Siendo realistas, es posible que pueda obtener un 1-2% general en ejemplos no inventados debido a una programación diferente y al uso de un par de instrucciones nuevas.
Damon
2
Solo porque dos procesadores pueden realizar las mismas operaciones, eso no significa que las operaciones tengan el mismo rendimiento en ambos procesadores ...
Mehrdad
Vea una pregunta mía relacionada en Stack Overflow: ¿Cómo funciona realmente mtune?
Marc.2377

Respuestas:

54

Porque diferentes generaciones de la misma arquitectura pueden tener diferentes conjuntos de instrucciones .

Por ejemplo, las extensiones SIMD de transmisión son probablemente el conjunto de instrucciones x86 más conocido, pero, a pesar de que solo hay una arquitectura x86, existen SSE, SSE2, SSE3 y SSE4.

Cada una de estas generaciones puede incluir nuevas instrucciones que proporcionan formas más rápidas de realizar ciertas operaciones. Un ejemplo que sería relevante para los juegos podría ser las instrucciones de productos de puntos.

Entonces, si un motor de juego se compila para una generación anterior de una arquitectura, no tendrá soporte para estas nuevas instrucciones. Del mismo modo, puede ser necesario optimizar el motor para obtener instrucciones más recientes; SSE4 , por ejemplo, tiene soporte para instrucciones de productos de puntos que funcionan en datos de matriz de estructuras. Una optimización que podría aprovechar estas nuevas instrucciones sería cambiar su diseño de datos a una matriz de estructuras.

Maximus Minimus
fuente
1
@Panzercrisis: gracias por la sugerencia de edición. Para ser claros: la pregunta original no era sobre su propio código, era sobre el código del motor, por lo que "optimizar su propio código" no es una buena sugerencia de edición. Sin embargo, resaltó que necesitaba aclarar que cuando dije "optimizar" quise decir "optimizar el código del motor", así que he editado para retomarlo.
Maximus Minimus
37

La respuesta de Maximus es correcta, solo quiero dar otra parte de la historia:

El hardware en sí cambia de una manera que necesita cambiar la forma en que codifica, independientemente de las instrucciones recién introducidas.

  • El aumento o la disminución de las cantidades de caché significa que debe preocuparse cada vez más por la optimización de la memoria caché / la invalidación de la memoria caché. Más caché significa que con datos pequeños puede centrarse menos en asegurarse de que los datos sean contiguos sin encontrarse con problemas de rendimiento. Menos caché significa que esto podría ser un problema, y ​​muy poco caché significa que con algunas estructuras de datos grandes no importará de ninguna manera.

  • Los nuevos niveles de caché significan que necesita pensar más sobre cómo organizar conjuntos de datos aún más grandes (L1, vs L2, vs L3 vs L4).

  • Más núcleos significa que debe pensar cómo va a mejorar las aplicaciones de subprocesos múltiples y cómo se escala su aplicación en un entorno de procesos múltiples.

  • Los relojes más rápidos significan que debe comenzar a pensar en la latencia de la memoria más de lo que necesita pensar en la velocidad de cálculo de la CPU como un cuello de botella de su sistema.

  • Es posible que el número de FPU en un sistema ya no coincida con el número de ALU enteras por núcleo (AMD tenía / tiene arquitecturas como esta).

  • El número de ciclos de reloj necesarios para calcular una operación puede haber disminuido o aumentado.

  • El número de registros disponibles cambió.

Todos estos tienen un impacto muy real en el rendimiento de los programas que hicieron suposiciones sobre la arquitectura subyacente en el hardware anterior con el mismo ISA, ya sea positivo o negativo.

cuando
fuente
"El aumento o la disminución de los niveles de caché significa que debe preocuparse menos por la coherencia del caché". Prácticamente cualquier CPU es coherente con el caché. ¿Te refieres al intercambio falso? Incluso casi cualquier línea de CPU $ es casi siempre 64 B ...
Maciej Piechotka
1
Maciej estaba tomando su declaración sobre la coherencia de la caché :) Probablemente quisiste decir "optimización de caché" o algo así. La coherencia de la memoria caché es la capacidad de un sistema de mantener una vista coherente de la memoria de forma transparente para el software, incluso en presencia de N memorias caché independientes . Esto es completamente ortogonal al tamaño. Por cierto, la afirmación no es realmente relevante, pero su respuesta (especialmente los puntos 5 y 6) aborda la pregunta mejor que la IMO aceptada :) Quizás resaltar la diferencia entre arquitectura y u-architecture hará que se destaque más.
Margaret Bloom
44
"como que la multiplicación lleva más tiempo que la suma, donde como hoy en día con la inteligencia moderna y el CPUS AMD, se necesita la misma cantidad de tiempo". En las arquitecturas canalizadas, debe diferenciar entre la latencia (cuando el resultado está listo) y el rendimiento (cuánto puede hacer por ciclo). Además, en los procesadores Intel modernos tiene un rendimiento de 4 y una latencia de 1. Multiply tiene un rendimiento 1 y una latencia 3 (o 4). Estas son las cosas que cambian con cada arquitectura y necesitan optimización. Por ejemplo, pdeptoma 1 ciclo en Intel pero 6 en Ryzen, por lo que es posible que no desee usarlo en Ryzen.
Christoph
2
@Clearer Sé que estamos hablando de CPU aquí, pero nunca has programado para GPU, ¿verdad? El mismo código produce resultados tan drásticamente diferentes en el rendimiento que a menudo se ve obligado a considerar las capacidades de hardware en CUDA. De ahí es de donde vine con esto, el tamaño de caché (memoria compartida, caché L1 administrada) en realidad debe tenerse en cuenta en la forma en que codifica algo en CUDA.
cuando
2
@ Christoph es correcto. El punto de referencia que vincula es para un bucle sobre una matriz c[i] = a[i] OP b[i](es decir, 2 cargas y 1 almacenamiento por operación), por lo que los tiempos están dominados por el ancho de banda de la memoria debido a la muy baja intensidad computacional. El tamaño de la matriz no se muestra tan IDK si cabe en L1D. ( gcc4.9 -Ofastmuy probablemente vectoriza automáticamente esos bucles, por lo que ni siquiera está midiendo el costo de las operaciones escalares normales como parte de un código entero complejo). La primera línea de esa página es IMPORTANTE: los comentarios útiles revelaron que algunas de estas medidas tienen serias fallas. Una actualización importante está en camino .
Peter Cordes
2

Incluso más allá de cambios importantes como el soporte para nuevas instrucciones, los fabricantes de microprocesadores están modificando constantemente sus diseños para mejorar el rendimiento, y cada nuevo diseño puede tener un rendimiento relativo diferente para cada instrucción o técnica. Tal vez escribió un código sin ramificaciones cuidadosamente optimizado para el Modelo X, pero el Modelo Y tiene un predictor de ramificación mejorado que reduce la penalización de predicción errónea para la versión del código sin ramificación (que también libera un registro para ser utilizado en otro lugar) . Tal vez el Modelo Y admite un mayor paralelismo de una determinada instrucción de alta latencia, de modo que ahora un bucle desenrollado de esa instrucción le brinda un mejor rendimiento, mientras que en el Modelo X una secuencia más corta era mejor.

Cualquier problema puede resolverse de muchas maneras, y cada programa es una colección entrelazada de compensaciones y asignaciones de recursos, desde el punto de optimización. Incluso pequeños cambios en la disponibilidad de esos recursos o el costo de una determinada pieza de código en términos de esos recursos, pueden tener un efecto en cascada que proporciona una ventaja de rendimiento sustancial para una pieza de código u otra. Incluso si un chip actualizado tiene "más de todo", cómo mucho más de cada cosa puede decantar la balanza.

hobbs
fuente