¿Qué técnica de mapeo de caché se utiliza en el procesador Intel Core i7?
He aprendido sobre diferentes técnicas de mapeo de caché, como el mapeo directo y el mapeo totalmente asociativo o asociativo por conjuntos, y las compensaciones entre ellas. ( Wikipedia )
Pero tengo curiosidad por saber cuál se utiliza hoy en día en los procesadores Intel Core i7 o AMD.
¿Cómo han evolucionado las técnicas? ¿Y qué cosas hay que mejorar?
Los cachés de mapeo directo básicamente nunca se utilizan en las CPU modernas de alto rendimiento . Los ahorros de energía se ven superados por la gran ventaja en la tasa de aciertos para un caché asociativo de conjunto del mismo tamaño, con sólo un poco más de complejidad en la lógica de control. Los presupuestos para transistores son muy grandes hoy en día.
Es muy común que el software tenga al menos un par de matrices que están separadas por un múltiplo de 4k entre sí, lo que crearía errores de conflicto en una caché asignada directamente. (Ajustar el código con más de un par de matrices puede implicar sesgarlas para reducir errores de conflicto, si un bucle necesita recorrerlas todas a la vez)
Las CPU modernas son tan rápidas que la latencia de la DRAM supera los 200 ciclos de reloj del núcleo, lo cual es demasiado grande incluso para que las CPU potentes con ejecución fuera de orden se oculten muy bien en caso de fallo de caché.
Los cachés multinivel son esenciales (y se utilizan en todas las CPU de alto rendimiento) para brindar baja latencia (~4 ciclos)/alto rendimiento para los datos más recientes (por ejemplo, hasta 2 cargas y 1 almacenamiento por reloj , con 128, 256 o incluso una ruta de 512 bits entre la caché L1D y las unidades de ejecución de carga/almacenamiento de vectores), sin dejar de ser lo suficientemente grande como para almacenar en caché un conjunto de trabajo de tamaño razonable. Es físicamente imposible crear una caché muy grande, muy rápida y altamente asociativa que funcione tan bien como las cachés multinivel actuales para cargas de trabajo típicas; Los retrasos en la velocidad de la luz cuando los datos tienen que viajar físicamente lejos son un problema. El costo de la energía también sería prohibitivo. (De hecho, la potencia/densidad de potencia es un factor limitante importante para las CPU modernas; consulte Microprocesadores modernos: ¡una guía de 90 minutos! ).
Todos los niveles de caché (excepto el caché uop) están físicamente indexados/etiquetados físicamente en todas las CPU x86 que conozco. Los cachés L1D en la mayoría de los diseños toman sus bits de índice debajo del desplazamiento de la página y, por lo tanto, también son VIPT, lo que permite que la búsqueda de TLB se realice en paralelo con la búsqueda de etiquetas, pero sin ningún problema de alias. Por lo tanto, no es necesario vaciar los cachés en cambios de contexto ni nada por el estilo. (Consulte esta respuesta para obtener más información sobre los cachés multinivel en general y el truco de velocidad VIPT, y algunos parámetros de caché de algunas CPU x86 reales).
Las cachés privadas (por núcleo) L1D/L1I y L2 son cachés asociativas de conjuntos tradicionales , a menudo de 8 o 4 vías para las cachés pequeñas/rápidas. El tamaño de la línea de caché es de 64 bytes en todas las CPU x86 modernas. Las cachés de datos son de reescritura. (Excepto en la familia AMD Bulldozer, donde L1D es de escritura directa con un pequeño búfer de combinación de escritura de 4 kiB).
http://www.7-cpu.com/ tiene buenos números de organización/latencia de caché, ancho de banda y números de organización/rendimiento de TLB, para varias microarquitecturas, incluidas muchas x86, como Haswell .
La caché uop decodificada "L0" en la familia Intel Sandybridge es asociativa por conjuntos y está direccionada virtualmente . Hasta 3 bloques de hasta 6 uops pueden almacenar en caché los resultados de decodificación de instrucciones en un bloque de código de máquina de 32 bytes. Relacionado: Alineación de ramas para bucles que involucran instrucciones microcodificadas en CPU de la familia Intel SnB . (Un caché uop es un gran avance para x86: las instrucciones x86 tienen una longitud variable y son difíciles de decodificar rápidamente/en paralelo, por lo que almacenar en caché los resultados de la decodificación interna, así como el código de máquina (L1I$), tiene importantes ventajas de potencia y rendimiento. Potente Todavía se necesitan decodificadores, porque el caché uop no es grande; es más efectivo en bucles (incluidos bucles medianos a grandes). Esto evita el error de Pentium4 (o la limitación basada en el tamaño del transitor en ese momento) de tener decodificadores débiles y depender de el caché de seguimiento.)
Los cachés modernos de Intel (y AMD, supongo) L3, también conocido como LLC, también conocidos como cachés de último nivel, utilizan una función de indexación que no es solo un rango de bits de direcciones . Es una función hash que distribuye mejor las cosas para reducir las colisiones de zancadas fijas. Según Intel, mi caché debería ser asociativo de 24 vías, aunque es de 12 vías, ¿cómo es eso? .
Desde Nehalem en adelante , Intel ha utilizado una gran caché L3 compartida inclusiva , que filtra el tráfico de coherencia entre núcleos . es decir, cuando un núcleo lee datos que están en estado Modificado en L1d de otro núcleo, las etiquetas L3 indican qué núcleo, por lo que se puede enviar una RFO (lectura de propiedad) solo a ese núcleo, en lugar de transmitirla. ¿Cómo están organizadas las cachés L3 de las CPU Intel modernas? . La propiedad de inclusión es importante porque significa que ningún caché privado L2 o L1 puede tener una copia de una línea de caché sin que L3 lo sepa. Si está en estado Exclusivo o Modificado en un caché privado, L3 tendrá datos no válidos para esa línea, pero las etiquetas aún indicarán qué núcleo podría tener una copia. A los núcleos que definitivamente no tienen una copia no es necesario que se les envíe un mensaje al respecto, lo que ahorra energía y ancho de banda a través de los enlaces internos entre los núcleos y L3. Consulte Por qué la coherencia de la caché en el chip llegó para quedarse (o con formato alternativo ) para obtener más detalles sobre la coherencia de la caché en el chip en Intel "i7" (es decir, las familias Nehalem y Sandybridge, que son arquitecturas diferentes pero utilizan la misma jerarquía de caché). ).
Core2Duo tenía un caché compartido de último nivel (L2), pero tardaba en generar solicitudes RFO (lectura de propiedad) en errores de L2. Entonces, el ancho de banda entre núcleos con un búfer pequeño que cabe en L1d es tan lento como con un búfer grande que no cabe en L2 (es decir, la velocidad de DRAM). Hay un rango rápido de tamaños cuando el búfer cabe en L2 pero no en L1d, porque el núcleo de escritura desaloja sus propios datos a L2, donde las cargas del otro núcleo pueden llegar sin generar una solicitud RFO. (Consulte la Figura 3.27: Ancho de banda de Core 2 con 2 subprocesos en "Lo que todo programador debería saber sobre la memoria" de Ulrich Drepper ( versión completa aquí ).
Skylake-AVX512 tiene L2 por núcleo más grande (1MiB en lugar de 256k) y porciones L3 (LLC) más pequeñas por núcleo. Ya no es inclusivo . Utiliza una red de malla en lugar de un bus en anillo para conectar los núcleos entre sí. Vea este artículo de AnandTech (pero tiene algunas imprecisiones en los detalles de microarquitectura en otras páginas, vea el comentario que dejé ).
De la descripción técnica de la familia escalable de procesadores Intel® Xeon®
Debido a la naturaleza no inclusiva de LLC , la ausencia de una línea de caché en LLC no indica que la línea no esté presente en las cachés privadas de ninguno de los núcleos. Por lo tanto, se utiliza un filtro de vigilancia para realizar un seguimiento de la ubicación de las líneas de caché en el L1 o MLC de los núcleos cuando no está asignado en el LLC. En las CPU de la generación anterior, la propia LLC compartida se encargaba de esta tarea.
Este "filtro de fisgoneo" sólo es útil si no puede tener falsos negativos. Está bien enviar una invalidación o RFO ( MESI ) a un núcleo que no tiene una copia de una línea. No está bien permitir que un núcleo conserve una copia de una línea cuando otro núcleo solicita acceso exclusivo a ella. Por lo tanto, puede ser un rastreador que incluye etiquetas y que sabe qué núcleos pueden tener copias de qué línea, pero que no almacena en caché ningún dato.
O tal vez el filtro snoop aún pueda ser útil sin incluir estrictamente todas las etiquetas L2/L1. No soy un experto en protocolos de vigilancia multinúcleo/multisocket. Creo que el mismo filtro de rastreo también puede ayudar a filtrar las solicitudes de rastreo entre sockets. (En Broadwell y versiones anteriores, solo los Xeon de cuatro sockets y superiores tienen un filtro de vigilancia para el tráfico entre núcleos; los Broadwell Xeon de doble socket y versiones anteriores no filtran las solicitudes de vigilancia entre los dos sockets ).
AMD Ryzen utiliza cachés L3 independientes para grupos de núcleos , por lo que los datos compartidos entre muchos núcleos deben duplicarse en L3 para cada grupo. También es importante que las escrituras desde un núcleo en un clúster tarden más en ser visibles para un núcleo en otro clúster, y las solicitudes de coherencia deben pasar por una interconexión entre clústeres. (Similar a entre sockets en un sistema Intel de múltiples sockets, donde cada paquete de CPU tiene su propio L3).
Esto nos da NUCA (Acceso a caché no uniforme), análogo al NUMA (Acceso a memoria no uniforme) habitual que se obtiene en un sistema de múltiples sockets donde cada procesador tiene un controlador de memoria incorporado y el acceso a la memoria local es más rápido que acceder a la memoria conectada a otro socket.
Los sistemas Intel multi-socket recientes tienen modos de vigilancia configurables, por lo que, en teoría, puede ajustar el mecanismo NUMA para que funcione mejor para la carga de trabajo que está ejecutando. Consulte la página de Intel sobre Broadwell-Xeon para obtener una tabla y una descripción de los modos de vigilancia disponibles.
Otro avance/evolución es una política de reemplazo adaptativo en la L3 en IvyBridge y posteriores . Esto puede reducir la contaminación cuando algunos datos tienen localidad temporal pero otras partes del conjunto de trabajo son mucho más grandes. (es decir, recorrer una matriz gigante con pseudo-LRU (que usan los cachés L1 y L2) desalojará todo, dejando la caché L3 solo almacenando en caché los datos de la matriz que no se volverán a tocar pronto. El reemplazo adaptativo intenta mitigar ese problema). Aparentemente , Intel usa alguna forma de reemplazo de LFU en estos días al menos en L1 y L2. (TODO: encuentre información más autorizada y detallada sobre esto).
Otras lecturas:
- ¿Cuánto de 'Lo que todo programador debería saber sobre la memoria' sigue siendo válido?
- ¿Por qué Skylake es mucho mejor que Broadwell-E en cuanto a rendimiento de memoria de un solo subproceso? ? (El ancho de banda de memoria de un solo subproceso en las CPU Xeon de muchos núcleos está limitado por max_concurrency/latency, no por el ancho de banda de DRAM).
- http://users.atw.hu/instlatx64/ para obtener resultados de sincronización del rendimiento de la memoria
- http://www.7-cpu.com/ para consultar la organización de caché/TLB y los números de latencia.
- http://agner.org/optimize/ para obtener detalles de microarquitectura (principalmente sobre el proceso de ejecución, no sobre la memoria) y guías de optimización de asm/C++.
- El wiki de etiquetas x86 de Stack Overflow tiene una sección de rendimiento, con enlaces a esas y más.