Las cargas no temporales y el precapturador de hardware, ¿funcionan juntos?

Al ejecutar una serie de llamadas _mm_stream_load_si128() ( MOVNTDQA ) desde ubicaciones de memoria consecutivas, el pre-fetcher de hardware aún se activará, o debería usar la captación previa de software explícita (con sugerencia NTA) para obtener los beneficios de la captación previa mientras aún ¿evitando la contaminación del escondite?

La razón por la que pregunto esto es porque sus objectives me parecen contradictorios. Una carga de transmisión obtendrá datos omitiendo la memoria caché, mientras que la captura previa intenta obtener datos de manera proactiva en la memoria caché.

Cuando se itera secuencialmente una estructura de datos grande (los datos procesados ​​no se retocarán en mucho tiempo), tendría sentido para mí evitar contaminar la jerarquía del chaché, pero no quiero incurrir en penalizaciones frecuentes de ~ 100 ciclos porque el pre -fetcher está inactivo.

La architecture de destino es Intel SandyBridge

Según la publicación de Patrick Fay (Intel) de noviembre de 2011: “En los procesadores Intel recientes, prefetchnta trae una línea de la memoria a la memoria caché de datos L1 (y no a los otros niveles de caché)”. También dice que debes asegurarte de que no realizas la captación previa demasiado tarde (la captación previa de HW ya la habrá llevado a todos los niveles), o demasiado pronto (desalojada cuando llegues allí).


Como se discutió en los comentarios sobre el OP, las CPU Intel actuales tienen un gran L3 compartido que incluye todos los cachés por núcleo. Esto significa que el tráfico de coherencia de caché solo tiene que verificar las tags L3 para ver si una línea de caché puede ser modificada en algún lugar en un L1 / L2 por núcleo.

IDK cómo conciliar la explicación de Pat Fay con mi comprensión de la coherencia de caché / jerarquía de caché. Pensé que si entraba en L1, también debería ir en L3. Tal vez las tags L1 tienen algún tipo de bandera para decir que esta línea está débilmente ordenada. Mi mejor suposición es que estaba simplificando, y diciendo L1 cuando en realidad solo va en búferes de relleno.

Esta guía de Intel sobre el trabajo con RAM de video habla de movimientos no temporales que utilizan almacenamientos intermedios de carga / tienda, en lugar de líneas de caché. (Tenga en cuenta que esto solo puede ser el caso de la memoria no recuperable ). No menciona la captación previa . También es viejo, anterior a SandyBridge. Sin embargo, tiene esta cita jugosa:

Las instrucciones de carga ordinaria extraen datos de la memoria del USWC en unidades del mismo tamaño que las instrucciones solicitadas. Por el contrario, una instrucción de carga de transmisión como MOVNTDQA comúnmente extraerá una línea de datos de caché completa a un “buffer de relleno” especial en la CPU. Las cargas de transmisión posteriores se leerían desde ese buffer de relleno, lo que implicaría un retraso mucho menor.

Y luego en otro párrafo, dice que las CPU típicas tienen de 8 a 10 buffers de relleno. SnB / Haswell todavía tiene 10 por núcleo. . Nuevamente, tenga en cuenta que esto solo se aplica a las regiones de memoria que no se pueden descartar.

movntdqa en la movntdqa WB (write-back) no está ordenada débilmente (ver la sección NT cargas de la respuesta vinculada) , por lo que no está permitido “añejo”. A diferencia de las tiendas NT, ni movntdqa ni prefetchnta cambian la semántica ordenando la memoria de la memoria Write-Back.

No probé esta suposición , pero prefetchnta / movntdqa en una CPU Intel moderna podría cargar una línea de caché en L3 y L1, pero podría omitir L2 (porque L2 no es inclusivo o exclusivo de L1). La sugerencia de NT podría tener un efecto al colocar la línea de caché en la posición LRU de su conjunto, donde es la siguiente línea desahuciada. (La política de caché normal inserta nuevas líneas en la posición MRU, más lejos de ser desalojado. Consulte este artículo sobre la política adaptativa L3 de IvB para obtener más información sobre la política de inserción de caché ).


El rendimiento de captura previa en IvyBridge es de solo uno por cada 43 ciclos, así que tenga cuidado de no captar demasiado si no desea que las búsquedas previas reduzcan la velocidad de su código en IvB. Fuente: tablas de ins de Agner Fog y guía de microarchivos . Este es un error de rendimiento específico de IvB. En otros diseños, demasiada captación previa simplemente tomará un rendimiento uop que podría haber sido instrucciones útiles (aparte del daño de la captación previa de direcciones inútiles).

Acerca de la captación previa de SW en general (no del tipo nt ): Linus Torvalds publicó acerca de cómo rara vez ayudan en el kernel de Linux, ya menudo hacen más daño que bien . Aparentemente, la recuperación previa de un puntero NULL al final de una lista enlazada puede causar una desaceleración, ya que intenta un relleno de TLB.

Esta pregunta me hizo leer … Mirando el manual de Intel para MOVNTDQA (usando una edición de Sep’14), hay una statement interesante:

Una implementación de procesador puede hacer uso de la sugerencia no temporal asociada con esta instrucción si la fuente de memoria es el tipo de memoria WC (combinación de escritura). Una implementación también puede hacer uso de la sugerencia no temporal asociada con esta instrucción si la fuente de memoria es tipo de memoria WB (write back).

y más tarde –

El tipo de memoria de la región que se lee puede anular la sugerencia no temporal, si la dirección de memoria especificada para la lectura no temporal no es una región de memoria WC.

Por lo tanto, parece que no hay garantía de que la sugerencia no temporal haga algo a menos que su tipo de memoria sea WC. Realmente no sé qué significa el comentario de memtype de WB, tal vez algunos procesadores de Intel le permiten usarlo para los beneficios de reducir la contaminación de caché, o tal vez querían mantener esta opción para el futuro (para que no empiece a usar MOVNTDQA en WB mem y supongo que siempre se comportaría igual), pero está bastante claro que WC mem es el caso de uso real aquí. Desea que esta instrucción proporcione algo de almacenamiento a corto plazo para cosas que de otro modo serían completamente descartables.

Ahora, por otro lado, mirando la descripción de captación previa *:

Las capturas previas de la memoria descatalogable o WC se ignoran.

Así que eso prácticamente cierra la historia: tu forma de pensar es absolutamente correcta, probablemente estos dos no estén diseñados y no funcionen juntos, es probable que uno de ellos sea ignorado.

Bien, pero ¿hay alguna posibilidad de que estos 2 realmente funcionen (si el procesador implementa cargas NT para la memoria WB)? Bueno, leyendo de MOVNTDQA otra vez, algo más llama la atención:

Cualquier línea con alias de tipo memoria en la memoria caché se censurará y enjuagará.

Ay. Por lo tanto, si de alguna manera logras captar previamente en tu caché, es probable que degrade el rendimiento de cualquier carga de transmisión consecutiva, ya que primero tendría que vaciar la línea. No es un pensamiento bonito.

Recientemente realicé algunas pruebas de varios sabores de prefetch previa al responder otra pregunta y mis hallazgos fueron:

Los resultados del uso de prefetchnta fueron consistentes con la siguiente implementación en el cliente de Skylake:

  • prefetchnta carga valores en L1 y L3 pero no en L2 (de hecho, parece que la línea puede ser desalojada de L2 si ya está allí).
  • Parece cargar el valor “normalmente” en L1, pero de una manera más débil en L3 de modo que se expulsa más rápidamente (por ejemplo, solo en una sola forma en el conjunto, o con su indicador LRU establecido de manera que sea el próxima víctima).
  • prefetchnta , como todas las demás instrucciones de captación previa, utilizan una entrada LFB, por lo que realmente no ayudan a obtener paralelismo adicional: pero la sugerencia de NTA puede ser útil aquí para evitar la contaminación L2 y L3.

El manual de optimización actual (248966-038) afirma en algunos lugares que prefetchnta trae datos al L2, pero solo en una forma fuera del conjunto. Por ejemplo, en 7.6.2.1 Video Encoder :

La gestión de caché de captación previa implementada para el codificador de video reduce el tráfico de la memoria. La reducción de la contaminación de caché de segundo nivel se garantiza al evitar que los datos de marcos de video de un solo uso ingresen a la memoria caché de segundo nivel. El uso de una instrucción PREFETCH (PREFETCHNTA) no temporal permite que los datos entren en una sola forma de la memoria caché de segundo nivel, reduciendo así la contaminación de la memoria caché de segundo nivel.

Esto no es consistente con los resultados de mi prueba en Skylake, donde caminar sobre una región de 64 KiB con prefetchnta muestra un rendimiento casi exactamente consistente con la obtención de datos del L3 (~ 4 ciclos por carga, con un factor MLP de 10 y una latencia L3 alrededor de 40 ciclos):

  Cycles ns 64-KiB parallel loads 1.00 0.39 64-KiB parallel prefetcht0 2.00 0.77 64-KiB parallel prefetcht1 1.21 0.47 64-KiB parallel prefetcht2 1.30 0.50 64-KiB parallel prefetchnta 3.96 1.53 

Como la L2 en Skylake es de 4 vías, si los datos se cargaron de una sola manera, apenas deberían permanecer en la memoria caché L2 (una forma de cobertura de 64 KiB), pero los resultados anteriores indican que no es así.

Puede ejecutar estas pruebas en su propio hardware en Linux utilizando mi progtwig uarch-bench . Los resultados de los sistemas antiguos serían particularmente interesantes.

Servidor Skylake (SKLX)

El comportamiento informado de prefetchnta en Skylake Server, que tiene una architecture de caché L3 diferente , es significativamente diferente del cliente de Skylake. En particular, el usuario Mysticial informa que las líneas captadas utilizando prefetchnta no están disponibles en ningún nivel de caché y deben volver a leerse desde DRAM una vez que se desalojen de L1.

La explicación más probable es que nunca ingresaron a L3 en absoluto como resultado de la prefetchnta ; esto es probable ya que en el servidor Skylake el L3 es un caché de víctimas compartidas no inclusivo para los cachés L2 privados, por lo que las líneas omiten el caché L2 usando prefetchnta es probable que nunca tengan la oportunidad de ingresar a la L3. Esto hace que prefetchnta sea ​​más pura en función: menos solicitudes de prefetchnta contaminan menos niveles de caché, pero también más frágil: cualquier falla al leer una línea nta desde L1 antes de ser desalojada significa otra ida y vuelta completa a la memoria: la solicitud inicial desencadenada por la prefetchnta es totalmente desperdiciado

Tanto MOVNTDQA (en la memoria WC) como PREFETCHNTA no afectan ni desencadenan ninguno de los MOVNTDQA hardware de caché. La idea general de la pista no temporal es evitar por completo la contaminación de la memoria caché o al menos minimizarla tanto como sea posible.

Solo hay un número muy pequeño (no documentado) de almacenamientos intermedios denominados almacenadores intermedios de carga de transmisión (estos están separados de los almacenamientos intermedios de relleno de línea y de la memoria caché L1) para mantener las líneas de caché MOVNTDQA mediante MOVNTDQA . Entonces, básicamente necesitas usar lo que traes casi de inmediato. Además, MOVNTDQA solo funciona en la memoria WC.

La instrucción PREFETCHNTA es perfecta para su escenario, pero debe descubrir cómo usarla correctamente en su código. Del manual de optimización Intel Sección 7.1:

Si su algoritmo es de paso único, use PREFETCHNTA. Si su algoritmo es de paso múltiple, use PREFETCHT0.

La instrucción PREFETCHNTA ofrece los siguientes beneficios:

  • Capta la línea de caché particular que contiene la dirección especificada en al menos la caché L3 y / o los niveles potencialmente más altos de la jerarquía de caché (consulte la respuesta de Bee y Peter y la Sección 7.3.2). En cada nivel de caché que se almacena en caché, podría / debería / más probablemente ser considerado el primero en ser desalojado en caso de que sea necesario desalojar una línea del conjunto. En una implementación de un algoritmo de paso único (como calcular el promedio de una gran cantidad de números) que se mejora con PREFETCHNTA , las líneas de caché extraídas previamente se pueden colocar en el mismo bloque que aquellas líneas que también se captaron PREFETCHNTA usando PREFETCHNTA . Por lo tanto, incluso si la cantidad total de datos que se obtienen es masiva, solo se verá afectada una forma de la memoria caché completa. Los datos que residen en las otras formas permanecerán en la memoria caché y estarán disponibles después de que termine el algoritmo. Pero esta es una espada de doble filo. Si dos instrucciones de PREFETCHNTA están demasiado cerca una de otra y si las direcciones especificadas se asignan al mismo conjunto de caché, solo una sobrevivirá.
  • Las líneas de caché captadas PREFETCHNTA con PREFETCHNTA se mantienen coherentes como cualquier otra línea en caché utilizando el mismo mecanismo de coherencia de hardware.
  • Funciona en los tipos de memoria WB, WC y WT. Lo más probable es que sus datos estén almacenados en la memoria WB.
  • Como dije antes, no desencadena la recuperación previa de hardware. Es por esta razón por la que también se puede utilizar para mejorar el rendimiento de los patrones de acceso a la memoria irregulares recomendados por Intel.

Es posible que el subproceso que ejecuta PREFETCHNTA no pueda beneficiarse eficazmente de él en función del comportamiento de cualquier otro subproceso en ejecución en el mismo núcleo físico, en otros núcleos físicos del mismo procesador o en núcleos de otros procesadores que comparten el mismo dominio de coherencia . Técnicas como, fijación, aumento de prioridad, partición de caché basada en CAT e inhabilitación de hyperthreading pueden ayudar a que el hilo se ejecute de manera eficiente. Tenga en cuenta también que PREFETCHNTA se clasifica como una carga especulativa y, por lo tanto, es concurrente con las tres instrucciones de la cerca.