¿Cómo se etiqueta la caché micro-op?

3

De acuerdo a Tecnologías del mundo real ’Artículo sobre“ Microarquitectura Sandy Bridge de Intel ”:

"El caché de uop de Sandy Bridge se organiza en 32 conjuntos y 8 formas, con 6 uops por línea, para un total de 1.5K uops de capacidad. El caché uop se incluye estrictamente en el caché de instrucciones L1. Cada línea también contiene metadatos que incluyen el número de uops válidos en la línea y la longitud de las instrucciones x86 correspondientes a la línea de caché uop. Cada ventana 32B que se asigna a la caché uop puede abarcar 3 de las 8 formas en un conjunto, por un máximo de 18 uops, aproximadamente 1.8B / uop. Si una ventana 32B tiene más de 18 uops, no puede caber en el caché uop y debe usar el front-end tradicional. Las instrucciones microcodificadas no se guardan en el caché uop, y en su lugar se representan mediante un puntero a la ROM de microcódigo y, opcionalmente, los primeros uops ".

'Cada ventana 32B (desde el caché de instrucciones) se asigna al caché uop, puede abarcar 3 de las 8 formas de un conjunto'

Entonces, supongamos que tenemos una ventana de instrucciones 32B que sería la mitad de una línea de caché de instrucciones L1, en esa línea, solo los bits de compensación serían diferentes, pero la etiqueta y los bits establecidos serían los mismos para todos los bytes en la línea.

Una vez que se ha decodificado una ventana de 32 bytes, los uops se ingresan en la caché uop con la misma dirección virtual que se usó para recuperar el bloque de recuperación de 16 bytes de la memoria caché de instrucciones L1 (para que puedan sondearse en paralelo en cada margen de 32B)

Dice que estos uops pueden abarcar 3 de las 8 formas en un conjunto, pero eso significaría que tendrían que tener los mismos bits de conjunto pero diferentes bits de etiqueta para terminar en el mismo conjunto (lo que significa que no habrían estado en la misma línea en el caché L1I), ¿significa esto que el caché uop está dispuesto de forma ligeramente diferente, una única dirección virtual al comienzo de una línea y los uops simplemente se llenan en la siguiente forma en el conjunto y la siguiente en la conjunto. ¿Cómo se garantiza que la siguiente ventana de instrucciones de 32B que aún tendría la misma etiqueta y los mismos conjuntos de bits pero diferentes bits de desplazamiento (la segunda mitad de la línea de 64 B en L1I) se asigne a la cuarta forma de ese conjunto?

Postulación : la forma de caché uop está etiquetada con una etiqueta física de índice virtual, la siguiente forma está etiquetada con nada, la tercera con nada, la 4ta está etiquetada con una etiqueta física / índice virtual donde la diferencia es que el desplazamiento ha cambiado de 0 a 32 En esencia, se puede seleccionar una forma utilizando diferentes bits de compensación en lugar de la manera en que se etiqueta la caché L1I: con los bits de compensación funcionando como una compensación de la línea de la memoria caché.

¿Alguien puede aclarar el diseño de las cachés uop o cómo funciona realmente este etiquetado?

Lewis Kelsey
fuente
Tenga en cuenta que AMD Zen también tiene un caché uop, pero se sabe menos sobre sus componentes internos. Así que estás preguntando específicamente sobre el caché uop de Intel en la familia Sandybridge. Según las pruebas de Agner Fog ( agner.org/optimize , específicamente su pdf de microarquía), se aborda virtualmente (VIVT), ahorrando la latencia / potencia de las búsquedas de iTLB.
Peter Cordes

Respuestas:

1

Tenga en cuenta que AMD Zen también tiene un caché uop, pero se sabe menos sobre sus componentes internos. Así que estás preguntando específicamente sobre el caché uop de Intel en la familia Sandybridge.

Según las pruebas de Agner Fog ( https://www.agner.org/optimize/ , específicamente su pdf de microarquía), está direccionado virtualmente (VIVT), guardando la latencia / potencia de las búsquedas de iTLB para los resultados de uop-cache. Y lo que hace posible integrar aún muy estrechamente el iTLB con el caché L1i, como es normal para un caché VIPT L1.

(También relacionado: ¿Qué técnica de mapeo de caché se usa en el procesador Intel Core i7? para un resumen de ese y otros cachés, y https://stackoverflow.com/tags/x86/info para más rendimiento / enlaces uarch.)

Una vez decodificada una ventana de 32 bytes.

Aquí es donde te equivocaste en tu proceso de pensamiento.

El caché de uop solo almacena en caché los uops que se decodifican a lo largo de la ruta de ejecución (especulativa). Las instrucciones x86 solo se pueden decodificar correctamente si conoce el punto de inicio correcto. Los bytes después de un incondicional. jmp podría no ser el comienzo de una instrucción en absoluto.

Además, no desea contaminar la caché uop con muchas instrucciones de relleno de un solo byte entre las funciones (por ejemplo, 0x90 NOP o 0xcc int3 utilizado por MSVC). O en general, con instrucciones "frías" que no se alcanzan durante la ejecución normal después de una rama tomada. Una "línea" / camino de uop-cache termina antes con un salto incondicional, o con un call.

Los decodificadores heredados son instrucciones de decodificación que la CPU espera ejecutar realmente (introduciéndolos en el caché uop para su reutilización más tarde, y el IDQ para su uso inmediato), o están apagados . A diferencia de P4, los decodificadores heredados no son débiles; son similares a los decodificadores en Core2 / Nehalem, por lo que la ejecución desde L1i generalmente está bien, excepto en el código de alto rendimiento con un tamaño de instrucción promedio grande. No necesitan tratar de "construir rastros" antes de tiempo. (El caché uop es no una caché de rastreo de todos modos; No sigue los saltos. Pero de todos modos, no intenta llenar el caché uop para los 32 bytes de instrucción que podría ser almacenado en caché de inmediato.)

Pero curiosamente, Agner dice " El mismo fragmento de código puede tener varias entradas en la memoria caché μop si tiene varias entradas de salto "


Mi mejor adivinar Cómo funciona realmente la maquinaria de búsqueda de caché:

Dada una dirección virtual de 64 bits para recuperar el código de:

  • Los 5 bits bajos son el desplazamiento relativo a un límite de 32 bytes.
  • Los siguientes 5 bits son un índice. No 6 bits para líneas L1i de 64 bytes; obtener de la caché uop no se preocupa directamente por eso.
  • Los bits más altos (hasta el bit 48) son la etiqueta.

Utilice el índice de 5 bits para seleccionar un conjunto.
Obtenga las 8 formas de ese conjunto (etiqueta + metadatos, y también datos en paralelo porque se trata de un caché de alto rendimiento).

Compara en paralelo para las 8 formas:

  • etiqueta bits todos coinciden
  • el desplazamiento está dentro del rango de inicio + longitud del código de máquina x86 de esta manera almacena en caché uops para. (Una forma solo puede almacenar en caché uops para 1 bloque contiguo de código de máquina x86).

A lo sumo, una forma en el conjunto tendrá ambas condiciones verdaderas para una dirección de instrucción dada. Si hay uno, este es tu hit, y puedes obtener uops de la forma que coincidió. (Al igual que con un caché de bytes regular, excepto que necesita verificar los metadatos para seleccionar de qué uop comenzar a buscar si saltó al medio de una manera).

Se trata de conjeturas basadas en cómo se realiza el caché de uop y cuando arroja formas. Pero puede ayudarte a obtener un modelo mental útil de ello.


Tenga en cuenta que la dirección no hace necesita ser 16 bytes alineados. Debe admitir de manera eficiente los objetivos de bifurcación que no estén alineados, así como el código de línea recta con límites de instrucción que no se alineen con los límites de 32 bytes. (Lo mejor que puedo decir es que las instrucciones que cruzan un límite de 32 bytes se almacenan en caché de forma uop-caché para la dirección de inicio de la instrucción, incluso si termina en la siguiente línea de caché L1i a través de un límite de 64 bytes).

Los bloques de recuperación / decodificación de L1i para la longitud de la instrucción están alineados, pero la decodificación completa en los decodificadores heredados funciona en hasta 16 bytes de cualquier alineación, tomada de la cola entre la decodificación y la decodificación. La alineación de los puntos de entrada del bucle a ciertos límites de alineación es menos importante de lo que solía ser.


Entonces supongo que hay una comprobación de que la dirección de recuperación coincide exactamente con una de las direcciones de inicio de instrucciones de la forma seleccionada. Esto no se admite de manera eficiente, porque solo el código confuso decodifica los mismos bytes de dos maneras diferentes.

La memoria caché de uop no puede almacenar ambas formas al mismo tiempo, por lo que, al detectar esto, la CPU tiene que recurrir a los decodificadores heredados y descartar las formas de caché uop para este bloque 32B (que ya detectó con el comparador de etiquetas).

Luego puede comenzar a rellenar nuevamente el uop-cache a medida que decodifica uops desde este punto.

Algo similar sucede cuando 3 formas ya están llenas, pero hay más uops del mismo bloque 32B de código de máquina x86. El uop-cache arroja las 3 formas para ese bloque. (No estoy seguro de si recuerda no intentar almacenarlos en la memoria caché para la próxima vez, o si simplemente crea la memoria caché cada vez y la tira cuando llega al límite, en un bucle con 20 bytes individuales). nop instrucciones por ejemplo.)

Ver Alineación de bifurcaciones para bucles que incluyen instrucciones microcodificadas en las CPU de la familia Intel SnB para algunos detalles sobre este caso . Tenga en cuenta que las instrucciones codificadas micro como div utilizan toda la forma del caché uop por sí mismos, y pueden llevar fácilmente a llenar los 3 modos y activar los conmutadores DSB a MITE (el caché uop a los conmutadores de decodificación heredados puede crear una burbuja de 1 ciclo en el extremo frontal) ).

Ese Q & amp; A tiene muchos experimentos detallados y conclusiones sobre cómo los uops se almacenan en caché. No tanto sobre cómo se implementa físicamente el caché uop; Eso es puramente conjeturas por mi parte aquí.

También tenga en cuenta que las CPU de Intel antes de Skylake solo pueden agregar 4 uops al IDQ desde el caché de uop, pero de alguna manera no obstaculizan cuando hay formas en el caché de uop que tienen 3 o 6 uops en lugar de 4. Entonces IDK si hay algún tipo del almacenamiento en búfer para la captura uop no ramificada. Esto es un poco de un misterio. Es de esperar que el patrón de 4, 2, 4, 2 se recupere si se obtiene de líneas completas de 6 uops cada una, pero no vemos un cuello de botella en el extremo frontal para los bucles que se ejecutan desde el caché uop con 2 -byte instrucciones como xor eax,eax. Intel ha declarado que el caché uop solo puede recuperar uops de 1 forma por ciclo, por lo que tal vez el límite de 4 uop sea solo para agregar al IDQ, no para leer desde el caché uop en algún búfer de combinación.

Peter Cordes
fuente
Gracias por esto, tenga en cuenta: 'El código pasa del búfer doble a los decodificadores en bloques que llamaré bloques IFETCH (bloques de obtención de instrucciones). Los bloques IFETCH tienen una longitud de hasta 16 bytes. En la mayoría de los casos, la unidad de búsqueda de instrucciones hace que cada bloque IFETCH comience en un límite de instrucción en lugar de en un límite de 16 bytes. - microarquitectura.pdf. Indica 'hasta 16 bytes'; para aclarar, esto significa que siempre garantiza que contiene instrucciones completas, por lo que si es 5,5,4,4, puede empaquetar los primeros 3 juntos y enviarlos como un bloque de 14 bytes y los próximos 4 comenzarán en un nuevo bloque ..?
Lewis Kelsey
... así que el rendimiento de la unidad de recuperación no siempre es de 16 bytes
Lewis Kelsey
@LewisKelsey: Oh, olvidé que había un almacenamiento en búfer antes de la decodificación previa, pero eso tiene sentido para mejorar el rendimiento y aprovechar al máximo el hardware que consume mucha energía y que está limitado a 16 bytes. De todos modos, recuerde que la CPU no sabe dónde terminan las instrucciones hasta que después antes de decodificar, o si la predicción de rama dice que hay una rama tomada, entonces se conoce el final de la misma. Pero de lo contrario, el front-end alimentará 16 bytes a los decodificadores previos. Si el último byte está en el medio de una inserción, entonces el inicio del siguiente bloque de predecodificación será el comienzo de esa instrucción (permanece hasta el próximo ciclo).
Peter Cordes
@LewisKelsey: De todos modos, sí, la decodificación previa debe encontrar el fin de una instrucción antes de que pueda enviarla a los decodificadores. Pero las partes de la guía uarch de Agner Fog que mencionan los bloques IFETCH son las secciones anteriores a Core2. Dice que Core2 agregó una cola entre la predicción de rama y la obtención de instrucciones. Pero él todavía dice " Cualquier instrucción que cruce un límite de 16 bytes se dejará hasta que se procese el siguiente bloque de 16 bytes. "por lo que la decodificación previa aún se basa en bloques alineados. (Pero la decodificación no lo es, incluso en CPU anteriores). Dato curioso: la versión previa a SnB, los decodificadores pueden hacer hasta 7 uops (4-1-1-1). SnB = 4.
Peter Cordes
Gracias, otro recurso útil es este: intel.co.uk/content/dam/www/public/us/en/documents/manuals/…
Lewis Kelsey