Una caché de CPU es una caché de hardware utilizada por la unidad central de procesamiento (CPU) de una computadora para reducir el costo promedio (tiempo o energía) para acceder a los datos desde la memoria principal . [1] Una caché es una memoria más pequeña y más rápida, ubicada más cerca del núcleo del procesador , que almacena copias de los datos de ubicaciones de memoria principal utilizadas con frecuencia . La mayoría de las CPU tienen una jerarquía de múltiples niveles de caché (L1, L2, a menudo L3 y rara vez incluso L4), con diferentes cachés específicos de instrucciones y de datos en el nivel 1. [2] La memoria caché generalmente se implementa con cachés aleatorios estáticos. memoria de acceso (SRAM), en las CPU modernas, con mucho, la mayor parte por área de chip, pero SRAM no siempre se usa para todos los niveles (de caché I o D), o incluso para cualquier nivel, a veces para alguno de los últimos o para todos los niveles. Se implementan con eDRAM .
Existen otros tipos de cachés (que no se cuentan para el "tamaño de caché" de los cachés más importantes mencionados anteriormente), como el búfer de traducción (TLB), que forma parte de la unidad de administración de memoria (MMU) que tienen la mayoría de las CPU.
Al intentar leer o escribir en una ubicación de la memoria principal, el procesador comprueba si los datos de esa ubicación ya están en la memoria caché. Si es así, el procesador leerá o escribirá en la caché en lugar de en la memoria principal, mucho más lenta.
Muchas CPU industriales , de servidores y de escritorio modernas tienen al menos tres cachés independientes:
Los primeros ejemplos de cachés de CPU incluyen el Atlas 2 [3] y el IBM System/360 Modelo 85 [4] [5] en la década de 1960. Las primeras CPU que utilizaron caché tenían solo un nivel de caché; a diferencia del caché de nivel 1 posterior, no se dividió en L1d (para datos) y L1i (para instrucciones). La caché L1 dividida comenzó en 1976 con la CPU IBM 801 , [6] [7] se generalizó a fines de la década de 1980 y en 1997 ingresó al mercado de CPU integradas con ARMv5TE. En 2015, incluso los SoC de menos de un dólar dividieron la caché L1. También tienen cachés L2 y, para procesadores más grandes, también cachés L3. La caché L2 generalmente no está dividida y actúa como un depósito común para la caché L1 ya dividida. Cada núcleo de un procesador multinúcleo tiene una caché L1 dedicada y, por lo general, no se comparte entre los núcleos. La caché L2 y las cachés de nivel superior se pueden compartir entre los núcleos. La caché L4 es poco común actualmente y generalmente es una memoria dinámica de acceso aleatorio (DRAM) en un chip o chip separado, en lugar de una memoria estática de acceso aleatorio (SRAM). Una excepción a esto es cuando se utiliza eDRAM para todos los niveles de caché, hasta L1. Históricamente, L1 también estaba en un dado separado, sin embargo, tamaños de dado más grandes han permitido la integración de este, así como de otros niveles de caché, con la posible excepción del último nivel. Cada nivel adicional de caché tiende a ser más grande y optimizarse de manera diferente.
Los cachés (como históricamente la RAM) generalmente se han dimensionado en potencias de: 2, 4, 8, 16, etc. KiB ; cuando se alcanzaron tamaños de MiB (es decir, para tamaños más grandes que no son L1), muy pronto el patrón se rompió, para permitir cachés más grandes sin verse obligados a adoptar el paradigma de duplicación de tamaño, con, por ejemplo, Intel Core 2 Duo con caché L2 de 3 MiB. en abril de 2008. Esto sucedió mucho más tarde para las cachés L1, ya que su tamaño generalmente sigue siendo una pequeña cantidad de KiB. Sin embargo, el IBM zEC12 de 2012 es una excepción, ya que obtiene un caché de datos L1 de 96 KiB inusualmente grande para su época y, por ejemplo, el IBM z13 tiene un caché de instrucciones L1 de 96 KiB (y un caché de datos L1 de 128 KiB), [8] e Intel Ice. Procesadores basados en Lake de 2018, con caché de datos L1 de 48 KiB y caché de instrucciones de 48 KiB L1. En 2020, algunas CPU Intel Atom (con hasta 24 núcleos) tienen (varios de) tamaños de caché de 4,5 MiB y 15 MiB. [9] [10]
Los datos se transfieren entre la memoria y el caché en bloques de tamaño fijo, llamados líneas de caché o bloques de caché . Cuando se copia una línea de caché de la memoria al caché, se crea una entrada de caché. La entrada de caché incluirá los datos copiados, así como la ubicación de memoria solicitada (llamada etiqueta).
Cuando el procesador necesita leer o escribir una ubicación en la memoria, primero busca una entrada correspondiente en la caché. El caché busca el contenido de la ubicación de memoria solicitada en cualquier línea de caché que pueda contener esa dirección. Si el procesador encuentra que la ubicación de la memoria está en el caché, se ha producido un acierto en el caché . Sin embargo, si el procesador no encuentra la ubicación de la memoria en el caché, se ha producido una pérdida de caché . En el caso de un acierto de caché, el procesador lee o escribe inmediatamente los datos en la línea de caché. En caso de fallo de caché, el caché asigna una nueva entrada y copia los datos de la memoria principal, luego la solicitud se cumple a partir del contenido del caché.
Para dejar espacio para la nueva entrada en un error de caché, es posible que el caché deba desalojar una de las entradas existentes. La heurística que utiliza para elegir la entrada a desalojar se llama política de reemplazo. El problema fundamental con cualquier política de reemplazo es que debe predecir qué entrada de caché existente tiene menos probabilidades de usarse en el futuro. Predecir el futuro es difícil, por lo que no existe un método perfecto para elegir entre la variedad de pólizas de reemplazo disponibles. Una política de reemplazo popular, la utilizada menos recientemente (LRU), reemplaza la entrada a la que se accedió menos recientemente.
Marcar algunos rangos de memoria como no almacenables en caché puede mejorar el rendimiento, al evitar el almacenamiento en caché de regiones de memoria a las que rara vez se vuelve a acceder. Esto evita la sobrecarga de cargar algo en el caché sin reutilizarlo. Las entradas de caché también pueden estar deshabilitadas o bloqueadas según el contexto.
Si los datos se escriben en la memoria caché, en algún momento también se deben escribir en la memoria principal; el momento de esta escritura se conoce como política de escritura. En una caché de escritura directa , cada escritura en la caché provoca una escritura en la memoria principal. Alternativamente, en una caché de reescritura o copia, las escrituras no se reflejan inmediatamente en la memoria principal y, en cambio, la caché rastrea qué ubicaciones se han sobrescrito y las marca como sucias . Los datos en estas ubicaciones se vuelven a escribir en la memoria principal solo cuando esos datos se eliminan del caché. Por esta razón, una lectura perdida en una caché de reescritura a veces puede requerir dos accesos a la memoria para el servicio: uno para escribir primero la ubicación sucia en la memoria principal y luego otro para leer la nueva ubicación de la memoria. Además, una escritura en una ubicación de memoria principal que aún no está asignada en una caché de reescritura puede desalojar una ubicación que ya está sucia, liberando así ese espacio de caché para la nueva ubicación de memoria.
También hay políticas intermedias. La caché puede ser de escritura directa, pero las escrituras pueden retenerse temporalmente en una cola de datos del almacén, normalmente para que se puedan procesar varios almacenes juntos (lo que puede reducir los tiempos de parada del bus y mejorar su utilización).
Los datos almacenados en caché de la memoria principal pueden ser modificados por otras entidades (por ejemplo, periféricos que utilizan acceso directo a la memoria (DMA) u otro núcleo en un procesador multinúcleo ), en cuyo caso la copia en el caché puede quedar obsoleta o duro. Alternativamente, cuando una CPU en un sistema multiprocesador actualiza datos en el caché, las copias de datos en cachés asociadas con otras CPU quedan obsoletas. Los protocolos de comunicación entre los administradores de caché que mantienen la coherencia de los datos se conocen como protocolos de coherencia de caché .
La medición del rendimiento de la caché se ha vuelto importante en los últimos tiempos, donde la brecha de velocidad entre el rendimiento de la memoria y el rendimiento del procesador está aumentando exponencialmente. El caché se introdujo para reducir esta brecha de velocidad. Por lo tanto, es importante saber qué tan bien la caché es capaz de cerrar la brecha entre la velocidad del procesador y la memoria, especialmente en sistemas de alto rendimiento. La tasa de aciertos de caché y la tasa de errores de caché juegan un papel importante en la determinación de este rendimiento. Para mejorar el rendimiento de la caché, reducir la tasa de errores se convierte en uno de los pasos necesarios, entre otros. Disminuir el tiempo de acceso al caché también aumenta su rendimiento y ayuda con la optimización.
El tiempo necesario para recuperar una línea de caché de la memoria ( latencia de lectura debido a una pérdida de caché) es importante porque la CPU se quedará sin trabajo mientras espera la línea de caché. Cuando una CPU alcanza este estado, se denomina parada. A medida que las CPU se vuelven más rápidas en comparación con la memoria principal, las paradas debidas a errores de caché desplazan más cálculos potenciales; Las CPU modernas pueden ejecutar cientos de instrucciones en el tiempo necesario para recuperar una sola línea de caché de la memoria principal.
Se han empleado varias técnicas para mantener ocupada la CPU durante este tiempo, incluida la ejecución fuera de orden en la que la CPU intenta ejecutar instrucciones independientes después de la instrucción que está esperando los datos perdidos de la caché. Otra tecnología, utilizada por muchos procesadores, es el subproceso múltiple simultáneo (SMT), que permite que un subproceso alternativo utilice el núcleo de la CPU mientras el primer subproceso espera a que los recursos de CPU necesarios estén disponibles.
La política de ubicación decide en qué lugar de la memoria caché irá una copia de una entrada particular de la memoria principal. Si la política de ubicación es libre de elegir cualquier entrada en el caché para guardar la copia, el caché se llama totalmente asociativo . En el otro extremo, si cada entrada de la memoria principal puede ir a un solo lugar de la caché, la caché se asigna directamente . Muchas cachés implementan un compromiso en el que cada entrada en la memoria principal puede ir a cualquiera de los N lugares en la caché y se describen como asociativos de conjuntos de N vías. [11] Por ejemplo, la caché de datos de nivel 1 en un AMD Athlon es asociativa de conjunto bidireccional, lo que significa que cualquier ubicación particular en la memoria principal se puede almacenar en caché en cualquiera de las dos ubicaciones en la caché de datos de nivel 1.
Elegir el valor correcto de asociatividad implica una compensación . Si hay diez lugares a los que la política de ubicación podría haber asignado una ubicación de memoria, entonces para comprobar si esa ubicación está en la memoria caché, se deben buscar diez entradas de la memoria caché. Verificar más lugares requiere más potencia y área de chip, y potencialmente más tiempo. Por otro lado, las cachés con más asociatividad sufren menos errores (consulte errores de conflicto ), de modo que la CPU pierde menos tiempo leyendo desde la memoria principal lenta. La pauta general es que duplicar la asociatividad, de mapeo directo a bidireccional, o de bidireccional a cuádruple, tiene aproximadamente el mismo efecto en aumentar la tasa de aciertos que duplicar el tamaño de la caché. Sin embargo, aumentar la asociatividad a más de cuatro no mejora tanto la tasa de aciertos [12] y generalmente se hace por otras razones (ver aliasing virtual). Algunas CPU pueden reducir dinámicamente la asociatividad de sus cachés en estados de bajo consumo, lo que actúa como una medida de ahorro de energía. [13]
En orden de peor pero simple a mejor pero complejo:
En esta organización de caché, cada ubicación en la memoria principal puede ir en una sola entrada en el caché. Por lo tanto, una caché de mapeo directo también puede denominarse caché "asociativa de conjunto unidireccional". No tiene una política de ubicación como tal, ya que no hay opción de qué contenido de entrada de caché desalojar. Esto significa que si dos ubicaciones se asignan a la misma entrada, es posible que continuamente se eliminen entre sí. Aunque es más simple, una caché de mapeo directo necesita ser mucho más grande que una asociativa para brindar un rendimiento comparable y es más impredecible. Sea x el número de bloque en la caché, y el número de bloque de la memoria y n el número de bloques en la caché, luego el mapeo se realiza con la ayuda de la ecuación x = y mod n .
Si cada ubicación de la memoria principal se puede almacenar en caché en cualquiera de las dos ubicaciones de la caché, una pregunta lógica es: ¿cuál de las dos? El esquema más simple y más comúnmente usado, que se muestra en el diagrama de la derecha arriba, es usar los bits menos significativos del índice de la ubicación de la memoria como índice para la memoria caché y tener dos entradas para cada índice. Un beneficio de este esquema es que las etiquetas almacenadas en la memoria caché no tienen que incluir la parte de la dirección de la memoria principal que está implícita en el índice de la memoria caché. Dado que las etiquetas de caché tienen menos bits, requieren menos transistores, ocupan menos espacio en la placa de circuito del procesador o en el chip del microprocesador y se pueden leer y comparar más rápido. Además, el algoritmo LRU es especialmente simple ya que sólo es necesario almacenar un bit para cada par.
Una de las ventajas de un caché de mapeo directo es que permite una especulación simple y rápida . Una vez que se ha calculado la dirección, se conoce el índice de caché que podría tener una copia de esa ubicación en la memoria. Esa entrada de caché se puede leer y el procesador puede continuar trabajando con esos datos antes de terminar de verificar que la etiqueta realmente coincida con la dirección solicitada.
La idea de que el procesador utilice los datos almacenados en caché antes de que se complete la coincidencia de etiquetas también se puede aplicar a los cachés asociativos. Se puede utilizar un subconjunto de la etiqueta, llamado sugerencia , para seleccionar solo una de las posibles entradas de caché asignadas a la dirección solicitada. La entrada seleccionada por la sugerencia se puede utilizar en paralelo con la verificación de la etiqueta completa. La técnica de la sugerencia funciona mejor cuando se utiliza en el contexto de la traducción de direcciones, como se explica a continuación.
Se han sugerido otros esquemas, como el caché sesgado , [14] donde el índice para la vía 0 es directo, como arriba, pero el índice para la vía 1 se forma con una función hash . Una buena función hash tiene la propiedad de que las direcciones que entran en conflicto con el mapeo directo tienden a no entrar en conflicto cuando se mapean con la función hash, por lo que es menos probable que un programa sufra una cantidad inesperadamente grande de conflictos fallidos debido a un acceso patológico. patrón. La desventaja es la latencia adicional al calcular la función hash. [15] Además, cuando llega el momento de cargar una nueva línea y desalojar una línea antigua, puede ser difícil determinar qué línea existente se usó menos recientemente, porque la nueva línea entra en conflicto con datos en diferentes índices en cada sentido; El seguimiento de LRU para cachés no sesgados generalmente se realiza por conjunto. Sin embargo, los cachés asociativos sesgados tienen grandes ventajas sobre los cachés asociativos de conjuntos convencionales. [16]
Una verdadera caché asociativa de conjuntos prueba todas las formas posibles simultáneamente, utilizando algo así como una memoria direccionable por contenido . Un caché pseudoasociativo prueba cada forma posible, una a la vez. Una caché hash-rehash y una caché asociativa de columnas son ejemplos de una caché pseudoasociativa.
En el caso común de encontrar un acierto en la primera forma probada, un caché pseudoasociativo es tan rápido como un caché de mapeo directo, pero tiene una tasa de errores de conflicto mucho menor que un caché de mapeo directo, más cercano a la tasa de errores. de un caché totalmente asociativo. [15]
En comparación con una caché de mapeo directo, una caché asociativa de conjunto tiene una cantidad reducida de bits para su índice de conjunto de caché que se asigna a un conjunto de caché, donde permanecen múltiples vías o bloques, como 2 bloques para una caché asociativa de conjunto de 2 vías y 4 bloques para un caché asociativo de 4 vías. En comparación con una caché asignada directamente, los bits de índice de la caché no utilizados pasan a formar parte de los bits de etiqueta. Por ejemplo, una caché asociativa de conjunto de 2 vías aporta 1 bit a la etiqueta y una caché asociativa de conjunto de 4 vías aporta 2 bits a la etiqueta. La idea básica del caché multicolumna [17] es usar el índice de conjunto para mapear un conjunto de caché como lo hace un caché asociativo de conjunto convencional, y usar los bits de etiqueta agregados para indexar una vía en el conjunto. Por ejemplo, en una caché asociativa de conjunto de 4 vías, los dos bits se utilizan para indexar la vía 00, la vía 01, la vía 10 y la vía 11, respectivamente. Esta indexación de doble caché se denomina "mapeo de ubicación principal" y su latencia es equivalente a un acceso mapeado directo. Amplios experimentos en el diseño de caché de varias columnas [17] muestran que la tasa de aciertos en las ubicaciones principales llega al 90%. Si el mapeo de caché entra en conflicto con un bloque de caché en la ubicación principal, el bloque de caché existente se moverá a otra forma de caché en el mismo conjunto, que se denomina "ubicación seleccionada". Debido a que el bloque de caché recién indexado es un bloque usado más recientemente (MRU), se coloca en la ubicación principal en el caché de varias columnas teniendo en cuenta la localidad temporal. Dado que la memoria caché de varias columnas está diseñada para una memoria caché con una alta asociatividad, el número de formas en cada conjunto es elevado; por lo tanto, es fácil encontrar una ubicación seleccionada en el conjunto. Se mantiene un índice de ubicación seleccionado mediante un hardware adicional para la ubicación principal en un bloque de caché. [ cita necesaria ]
La caché de varias columnas sigue teniendo una alta tasa de aciertos debido a su alta asociatividad y tiene una baja latencia comparable a la de una caché de mapeo directo debido a su alto porcentaje de aciertos en las ubicaciones principales. Los conceptos de ubicaciones principales y ubicaciones seleccionadas en caché de varias columnas se han utilizado en varios diseños de caché en el chip ARM Cortex R, [18] la memoria caché de predicción de vías de Intel, [19] la memoria caché asociativa multidireccional reconfigurable de IBM [20] y la Selección de forma de reemplazo de caché dinámica basada en bits de pestaña de dirección. [21]
Las entradas de las filas de caché suelen tener la siguiente estructura:
El bloque de datos (línea de caché) contiene los datos reales obtenidos de la memoria principal. La etiqueta contiene (parte de) la dirección de los datos reales obtenidos de la memoria principal. Los bits de bandera se analizan a continuación.
El "tamaño" del caché es la cantidad de datos de la memoria principal que puede contener. Este tamaño se puede calcular como la cantidad de bytes almacenados en cada bloque de datos multiplicada por la cantidad de bloques almacenados en la caché. (Los bits de etiqueta, indicador y código de corrección de errores no están incluidos en el tamaño, [22] aunque sí afectan el área física de un caché).
Una dirección de memoria efectiva que va junto con la línea de caché (bloque de memoria) se divide ( MSB a LSB ) en la etiqueta, el índice y el desplazamiento del bloque. [23] [24]
El índice describe en qué conjunto de caché se han colocado los datos. La longitud del índice es de bits para s conjuntos de caché.
El desplazamiento del bloque especifica los datos deseados dentro del bloque de datos almacenado dentro de la fila de caché. Normalmente, la dirección efectiva está en bytes, por lo que la longitud del desplazamiento del bloque es en bits, donde b es el número de bytes por bloque de datos. La etiqueta contiene los bits más significativos de la dirección, que se comparan con todas las filas del conjunto actual (el conjunto se ha recuperado por índice) para ver si este conjunto contiene la dirección solicitada. Si es así, se produce un acierto de caché. La longitud de la etiqueta en bits es la siguiente:
tag_length = address_length - index_length - block_offset_length
Algunos autores se refieren al desplazamiento del bloque simplemente como el "desplazamiento" [25] o el "desplazamiento". [26] [27]
El procesador Pentium 4 original tenía un caché de datos L1 asociativo de cuatro vías de 8 KiB de tamaño, con bloques de caché de 64 bytes. Por lo tanto, hay 8 KiB/64 = 128 bloques de caché. El número de conjuntos es igual al número de bloques de caché dividido por el número de formas de asociatividad, lo que da como resultado 128/4 = 32 conjuntos y, por tanto, 2 · 5 = 32 índices diferentes. Hay 2 6 = 64 compensaciones posibles. Dado que la dirección de la CPU tiene 32 bits de ancho, esto implica 32 - 5 - 6 = 21 bits para el campo de etiqueta.
El procesador Pentium 4 original también tenía un caché integrado L2 asociativo de ocho vías de 256 KiB de tamaño, con bloques de caché de 128 bytes. Esto implica 32 - 8 - 7 = 17 bits para el campo de etiqueta. [25]
Un caché de instrucciones requiere sólo un bit de bandera por cada entrada de fila del caché: un bit válido. El bit válido indica si un bloque de caché se ha cargado con datos válidos.
Al encenderse, el hardware establece todos los bits válidos en todos los cachés como "no válidos". Algunos sistemas también configuran un bit válido como "no válido" en otros momentos, como cuando el hardware de espionaje de bus multimaestro en el caché de un procesador escucha una dirección transmitida desde algún otro procesador y se da cuenta de que ciertos bloques de datos en el caché local están ahora está obsoleto y debe marcarse como no válido.
Una caché de datos normalmente requiere dos bits de bandera por línea de caché: un bit válido y un bit sucio . Tener un bit sucio establecido indica que la línea de caché asociada ha cambiado desde que se leyó desde la memoria principal ("sucia"), lo que significa que el procesador ha escrito datos en esa línea y el nuevo valor no se ha propagado hasta la memoria principal. .
Un error de caché es un intento fallido de leer o escribir un dato en el caché, lo que resulta en un acceso a la memoria principal con una latencia mucho más larga. Hay tres tipos de errores de caché: errores de lectura de instrucciones, errores de lectura de datos y errores de escritura de datos.
Los errores de lectura de caché de una caché de instrucciones generalmente causan el mayor retraso, porque el procesador, o al menos el hilo de ejecución , tiene que esperar (detenerse) hasta que la instrucción se recupera de la memoria principal. Los errores de lectura de caché de un caché de datos generalmente causan un retraso menor, porque se pueden emitir instrucciones que no dependen de la lectura del caché y continuar la ejecución hasta que los datos se devuelvan desde la memoria principal y las instrucciones dependientes puedan reanudar la ejecución. Los errores de escritura en caché en una caché de datos generalmente causan el retraso más corto, porque la escritura se puede poner en cola y existen pocas limitaciones en la ejecución de instrucciones posteriores; el procesador puede continuar hasta que la cola esté llena. Para obtener una introducción detallada a los tipos de errores, consulte Medición y métrica del rendimiento de la caché .
La mayoría de las CPU de uso general implementan algún tipo de memoria virtual . En resumen, cada programa que se ejecuta en la máquina ve su propio espacio de direcciones simplificado , que contiene código y datos solo para ese programa, o todos los programas se ejecutan en un espacio de direcciones virtuales común. Un programa se ejecuta calculando, comparando, leyendo y escribiendo direcciones de su espacio de direcciones virtuales, en lugar de direcciones del espacio de direcciones físico, lo que hace que los programas sean más simples y, por lo tanto, más fáciles de escribir.
La memoria virtual requiere que el procesador traduzca las direcciones virtuales generadas por el programa en direcciones físicas en la memoria principal. La parte del procesador que realiza esta traducción se conoce como unidad de gestión de memoria (MMU). La ruta rápida a través de la MMU puede realizar aquellas traducciones almacenadas en el búfer de búsqueda de traducción (TLB), que es un caché de asignaciones de la tabla de páginas , la tabla de segmentos o ambas del sistema operativo .
A los efectos de la presente discusión, existen tres características importantes de la traducción de direcciones:
Uno de los primeros sistemas de memoria virtual, el IBM M44/44X , requería acceso a una tabla de mapeo mantenida en la memoria central antes de cada acceso programado a la memoria principal. [28] [NB 1] Sin cachés y con la memoria de la tabla de mapeo ejecutándose a la misma velocidad que la memoria principal, esto efectivamente reduce la velocidad de acceso a la memoria a la mitad. Dos de las primeras máquinas que utilizaban una tabla de páginas en la memoria principal para el mapeo, el IBM System/360 Modelo 67 y el GE 645 , tenían una pequeña memoria asociativa como caché para los accesos a la tabla de páginas en memoria. Ambas máquinas son anteriores a la primera máquina con un caché para la memoria principal, el IBM System/360 Modelo 85 , por lo que el primer caché de hardware utilizado en un sistema informático no fue un caché de datos o instrucciones, sino más bien un TLB.
Los cachés se pueden dividir en cuatro tipos, según si el índice o la etiqueta corresponden a direcciones físicas o virtuales:
La velocidad de esta recurrencia (la latencia de carga ) es crucial para el rendimiento de la CPU, por lo que la mayoría de los cachés de nivel 1 modernos están virtualmente indexados, lo que al menos permite que la búsqueda de TLB de la MMU continúe en paralelo con la recuperación de los datos de la RAM del caché.
Pero la indexación virtual no es la mejor opción para todos los niveles de caché. El costo de tratar con alias virtuales aumenta con el tamaño de la caché y, como resultado, la mayoría de las cachés de nivel 2 y mayores están indexadas físicamente.
Históricamente, los cachés han utilizado direcciones físicas y virtuales para las etiquetas de caché, aunque el etiquetado virtual ahora es poco común. Si la búsqueda de TLB puede finalizar antes que la búsqueda de RAM de caché, entonces la dirección física estará disponible a tiempo para la comparación de etiquetas y no hay necesidad de etiquetado virtual. Por lo tanto, las cachés grandes tienden a etiquetarse físicamente, y sólo las cachés pequeñas y de muy baja latencia se etiquetan virtualmente. En las CPU de uso general recientes, el etiquetado virtual ha sido reemplazado por vhints, como se describe a continuación.
Un caché que se basa en indexación y etiquetado virtual se vuelve inconsistente después de que la misma dirección virtual se asigna a diferentes direcciones físicas ( homónimo ), lo que se puede resolver usando una dirección física para el etiquetado o almacenando el identificador del espacio de direcciones en la línea de caché. Sin embargo, este último enfoque no ayuda contra el problema de sinónimos , en el que varias líneas de caché terminan almacenando datos para la misma dirección física. Escribir en dichas ubicaciones puede actualizar solo una ubicación en la caché, dejando a las demás con datos inconsistentes. Este problema se puede resolver utilizando diseños de memoria que no se superpongan para diferentes espacios de direcciones o, de lo contrario, se debe vaciar la caché (o una parte de ella) cuando cambia la asignación. [34]
La gran ventaja de las etiquetas virtuales es que, para las cachés asociativas, permiten que la coincidencia de etiquetas continúe antes de que se realice la traducción de virtual a física. Sin embargo, las investigaciones de coherencia y los desalojos presentan un lugar físico para actuar. El hardware debe tener algún medio para convertir las direcciones físicas en un índice de caché, generalmente almacenando etiquetas físicas y etiquetas virtuales. A modo de comparación, una caché etiquetada físicamente no necesita mantener etiquetas virtuales, lo cual es más sencillo. Cuando se elimina una asignación de virtual a física del TLB, las entradas de la caché con esas direcciones virtuales deberán vaciarse de alguna manera. Alternativamente, si se permiten entradas de caché en páginas no asignadas por el TLB, esas entradas deberán eliminarse cuando se cambien los derechos de acceso a esas páginas en la tabla de páginas.
También es posible que el sistema operativo garantice que ningún alias virtual resida simultáneamente en la memoria caché. El sistema operativo ofrece esta garantía aplicando el color de página, que se describe a continuación. Algunos de los primeros procesadores RISC (SPARC, RS/6000) adoptaron este enfoque. No se ha utilizado recientemente, ya que el costo del hardware para detectar y desalojar alias virtuales ha disminuido y la complejidad del software y la penalización en el rendimiento del coloreado perfecto de las páginas han aumentado.
Puede resultar útil distinguir las dos funciones de las etiquetas en una memoria caché asociativa: se utilizan para determinar qué forma del conjunto de entradas seleccionar y se utilizan para determinar si la memoria caché acertó o no. La segunda función siempre debe ser correcta, pero está permitido que la primera función adivine y obtenga la respuesta incorrecta ocasionalmente.
Algunos procesadores (por ejemplo, los primeros SPARC) tienen cachés con etiquetas físicas y virtuales. Las etiquetas virtuales se utilizan para la selección de caminos y las etiquetas físicas se utilizan para determinar el acierto o el error. Este tipo de caché disfruta de la ventaja de latencia de un caché etiquetado virtualmente y de la sencilla interfaz de software de un caché etiquetado físicamente. Sin embargo, conlleva el coste adicional de las etiquetas duplicadas. Además, durante el procesamiento de errores, se deben buscar alias virtuales en las formas alternativas de la línea de caché indexada y se deben desalojar las coincidencias.
El área adicional (y algo de latencia) se puede mitigar manteniendo sugerencias virtuales con cada entrada de caché en lugar de etiquetas virtuales. Estas sugerencias son un subconjunto o hash de la etiqueta virtual y se utilizan para seleccionar la forma del caché del cual obtener datos y una etiqueta física. Al igual que un caché etiquetado virtualmente, puede haber una coincidencia de sugerencia virtual pero una discrepancia de etiqueta física, en cuyo caso la entrada de caché con la sugerencia coincidente debe ser desalojada para que los accesos al caché después del llenado de caché en esta dirección tengan solo una coincidencia de sugerencia. Dado que las sugerencias virtuales tienen menos bits que las etiquetas virtuales que las distinguen entre sí, una caché con sugerencias virtuales sufre más errores de conflicto que una caché con etiquetas virtuales.
Quizás la reducción definitiva de pistas virtuales se pueda encontrar en el Pentium 4 (núcleos Willamette y Northwood). En estos procesadores, la pista virtual es efectivamente de dos bits y el caché es asociativo de cuatro vías. Efectivamente, el hardware mantiene una permutación simple de dirección virtual a índice de caché, de modo que no se necesita memoria direccionable por contenido (CAM) para seleccionar la correcta de las cuatro formas de recuperación.
Los cachés grandes indexados físicamente (generalmente cachés secundarios) tienen un problema: el sistema operativo, en lugar de la aplicación, controla qué páginas chocan entre sí en el caché. Las diferencias en la asignación de páginas de un programa ejecutado al siguiente dan lugar a diferencias en los patrones de colisión de la caché, lo que puede generar diferencias muy grandes en el rendimiento del programa. Estas diferencias pueden hacer que sea muy difícil conseguir un tiempo consistente y repetible para una ejecución de referencia.
Para comprender el problema, considere una CPU con una caché de nivel 2 de mapeo directo indexada físicamente de 1 MiB y páginas de memoria virtual de 4 KiB. Las páginas físicas secuenciales se asignan a ubicaciones secuenciales en la caché hasta que después de 256 páginas el patrón se ajusta. Podemos etiquetar cada página física con un color de 0 a 255 para indicar en qué parte del caché puede ir. Las ubicaciones dentro de páginas físicas con diferentes colores no pueden entrar en conflicto en la memoria caché.
Los programadores que intentan aprovechar al máximo la caché pueden organizar los patrones de acceso de sus programas de modo que sólo sea necesario almacenar en caché 1 MiB de datos en un momento dado, evitando así pérdidas de capacidad. Pero también deben garantizar que los patrones de acceso no tengan errores conflictivos. Una forma de pensar en este problema es dividir las páginas virtuales que utiliza el programa y asignarles colores virtuales de la misma manera que antes se asignaban los colores físicos a las páginas físicas. Luego, los programadores pueden organizar los patrones de acceso de su código para que no se utilicen dos páginas con el mismo color virtual al mismo tiempo. Existe una amplia literatura sobre este tipo de optimizaciones (por ejemplo, optimización de nidos de bucles ), procedente en gran medida de la comunidad de informática de alto rendimiento (HPC) .
El problema es que, si bien todas las páginas en uso en un momento dado pueden tener diferentes colores virtuales, algunas pueden tener los mismos colores físicos. De hecho, si el sistema operativo asigna páginas físicas a páginas virtuales de manera aleatoria y uniforme, es muy probable que algunas páginas tengan el mismo color físico y luego las ubicaciones de esas páginas colisionarán en el caché (esta es la paradoja del cumpleaños ).
La solución es hacer que el sistema operativo intente asignar diferentes colores físicos a diferentes colores virtuales, una técnica llamada coloración de páginas . Aunque la asignación real del color virtual al físico es irrelevante para el rendimiento del sistema, las asignaciones impares son difíciles de seguir y tienen pocos beneficios, por lo que la mayoría de los enfoques para colorear páginas simplemente intentan mantener iguales los colores de las páginas físicas y virtuales.
Si el sistema operativo puede garantizar que cada página física se asigne a un solo color virtual, entonces no hay alias virtuales y el procesador puede usar cachés virtualmente indexadas sin necesidad de sondas de alias virtuales adicionales durante el manejo de errores. Alternativamente, el sistema operativo puede eliminar una página del caché cada vez que cambia de un color virtual a otro. Como se mencionó anteriormente, este enfoque se utilizó para algunos de los primeros diseños SPARC y RS/6000.
La técnica de coloración de páginas de software se ha utilizado para particionar eficazmente la caché de último nivel (LLC) compartida en procesadores multinúcleo. [35] Intel ha adoptado esta gestión LLC basada en el sistema operativo en procesadores multinúcleo. [36]
Los procesadores modernos tienen múltiples cachés en chip que interactúan. El funcionamiento de una caché particular se puede especificar completamente mediante el tamaño de la caché, el tamaño del bloque de la caché, el número de bloques en un conjunto, la política de reemplazo del conjunto de caché y la política de escritura de la caché (escritura directa o reescritura). [25]
Si bien todos los bloques de caché en un caché particular son del mismo tamaño y tienen la misma asociatividad, típicamente los cachés de "nivel inferior" (llamados caché de nivel 1) tienen una cantidad menor de bloques, un tamaño de bloque más pequeño y menos bloques en un caché. establecidos, pero tienen tiempos de acceso muy cortos. Las cachés de "nivel superior" (es decir, nivel 2 y superiores) tienen progresivamente un mayor número de bloques, un tamaño de bloque mayor, más bloques en un conjunto y tiempos de acceso relativamente más largos, pero siguen siendo mucho más rápidos que la memoria principal.
La política de reemplazo de entradas de caché está determinada por un algoritmo de caché seleccionado para ser implementado por los diseñadores del procesador. En algunos casos, se proporcionan varios algoritmos para diferentes tipos de cargas de trabajo.
Las CPU canalizadas acceden a la memoria desde múltiples puntos de la canalización : recuperación de instrucciones, traducción de direcciones virtuales a físicas y recuperación de datos (consulte canalización RISC clásica ). El diseño natural es utilizar diferentes cachés físicas para cada uno de estos puntos, de modo que no sea necesario programar ningún recurso físico para dar servicio a dos puntos de la tubería. Por lo tanto, la canalización termina naturalmente con al menos tres cachés separados (instrucciones, TLB y datos), cada uno de ellos especializado en su función particular.
Un caché de víctima es un caché que se utiliza para contener bloques desalojados de un caché de CPU tras su reemplazo. La caché de la víctima se encuentra entre la caché principal y su ruta de recarga, y contiene solo aquellos bloques de datos que fueron desalojados de la caché principal. La caché de víctimas suele ser totalmente asociativa y tiene como objetivo reducir el número de fallos de conflicto. Muchos programas de uso común no requieren un mapeo asociativo para todos los accesos. De hecho, sólo una pequeña fracción de los accesos a la memoria del programa requieren una alta asociatividad. La caché de la víctima explota esta propiedad proporcionando una alta asociatividad sólo a estos accesos. Fue introducido por Norman Jouppi de DEC en 1990. [37]
La variante Crystalwell [38] de Intel de sus procesadores Haswell introdujo un caché eDRAM de nivel 4 de 128 MiB en el paquete que sirve como caché de víctima para el caché de nivel 3 de los procesadores. [39] En la microarquitectura Skylake, el caché de nivel 4 ya no funciona como caché de víctima. [40]
Uno de los ejemplos más extremos de especialización de caché es el caché de seguimiento (también conocido como caché de seguimiento de ejecución ) que se encuentra en los microprocesadores Intel Pentium 4 . Un caché de seguimiento es un mecanismo para aumentar el ancho de banda de búsqueda de instrucciones y disminuir el consumo de energía (en el caso del Pentium 4) mediante el almacenamiento de rastros de instrucciones que ya han sido recuperadas y decodificadas. [41]
Un caché de seguimiento almacena instrucciones después de haberlas decodificado o cuando se retiran. Generalmente, las instrucciones se agregan a las cachés de seguimiento en grupos que representan bloques básicos individuales o seguimientos de instrucciones dinámicas. La caché de seguimiento del Pentium 4 almacena microoperaciones resultantes de la decodificación de instrucciones x86, proporcionando también la funcionalidad de una caché de microoperaciones. Teniendo esto, la próxima vez que se necesite una instrucción, no será necesario volver a decodificarla en microoperaciones. [42] : 63–68
Write Coalescing Cache [43] es un caché especial que forma parte del caché L2 en la microarquitectura Bulldozer de AMD . Los almacenes de ambos cachés L1D en el módulo pasan por el WCC, donde se almacenan en buffer y se fusionan. La tarea del WCC es reducir el número de escrituras en la caché L2.
Un caché de microoperaciones ( μop cache , uop cache o UC ) [44] es un caché especializado que almacena microoperaciones de instrucciones decodificadas, tal como se reciben directamente de los decodificadores de instrucciones o del caché de instrucciones. Cuando es necesario decodificar una instrucción, se verifica la forma decodificada del caché μop, que se reutiliza si se almacena en caché; si no está disponible, la instrucción se decodifica y luego se almacena en caché.
Uno de los primeros trabajos que describe el caché μop como una interfaz alternativa para la familia de procesadores Intel P6 es el artículo de 2001 "Micro-Operation Cache: A Power Aware Frontend for Variable Instrucciones Longitud ISA" . [45] Posteriormente, Intel incluyó cachés μop en sus procesadores Sandy Bridge y en sucesivas microarquitecturas como Ivy Bridge y Haswell . [42] : 121–123 [46] AMD implementó un caché μop en su microarquitectura Zen . [47]
La recuperación de instrucciones predecodificadas completas elimina la necesidad de decodificar repetidamente instrucciones complejas de longitud variable en microoperaciones más simples de longitud fija y simplifica el proceso de predicción, recuperación, rotación y alineación de instrucciones recuperadas. Una caché μop descarga eficazmente el hardware de recuperación y decodificación, lo que reduce el consumo de energía y mejora el suministro frontal de microoperaciones decodificadas. La caché μop también aumenta el rendimiento al entregar de manera más consistente microoperaciones decodificadas al backend y eliminando varios cuellos de botella en la lógica de recuperación y decodificación de la CPU. [45] [46]
Una caché μop tiene muchas similitudes con una caché de seguimiento, aunque una caché μop es mucho más simple y, por lo tanto, proporciona una mejor eficiencia energética; esto lo hace más adecuado para implementaciones en dispositivos que funcionan con baterías. La principal desventaja del caché de seguimiento, que conduce a su ineficiencia energética, es la complejidad del hardware requerida para que su heurística decida el almacenamiento en caché y la reutilización de seguimientos de instrucciones creados dinámicamente. [48]
Un caché de destino de rama o caché de instrucciones de destino de rama , el nombre utilizado en los microprocesadores ARM , [49] es un caché especializado que contiene las primeras instrucciones en el destino de una rama tomada. Esto lo utilizan procesadores de baja potencia que no necesitan un caché de instrucciones normal porque el sistema de memoria es capaz de entregar instrucciones lo suficientemente rápido como para satisfacer la CPU sin uno. Sin embargo, esto sólo se aplica a instrucciones consecutivas en secuencia; Todavía se necesitan varios ciclos de latencia para reiniciar la búsqueda de instrucciones en una nueva dirección, lo que provoca algunos ciclos de burbuja en la tubería después de una transferencia de control. Un caché de destino de rama proporciona instrucciones para esos pocos ciclos evitando un retraso después de la mayoría de las ramas tomadas.
Esto permite un funcionamiento a máxima velocidad con una caché mucho más pequeña que una caché de instrucciones tradicional de tiempo completo.
La memoria caché inteligente es un método de almacenamiento en caché de nivel 2 o 3 para múltiples núcleos de ejecución, desarrollado por Intel .
Smart Cache comparte la memoria caché real entre los núcleos de un procesador multinúcleo . En comparación con una caché dedicada por núcleo, la tasa general de errores de caché disminuye cuando los núcleos no requieren partes iguales del espacio de caché. En consecuencia, un solo núcleo puede utilizar la caché de nivel 2 o 3 completa mientras los otros núcleos están inactivos. [50] Además, el caché compartido hace que sea más rápido compartir memoria entre diferentes núcleos de ejecución. [51]
Otro problema es el equilibrio fundamental entre la latencia de la caché y la tasa de aciertos. Los cachés más grandes tienen mejores tasas de aciertos pero una latencia más larga. Para abordar esta desventaja, muchas computadoras utilizan múltiples niveles de caché, con cachés pequeños y rápidos respaldados por cachés más grandes y más lentos. Las cachés multinivel generalmente funcionan comprobando primero la caché más rápida, el nivel 1 ( L1 ); si golpea, el procesador avanza a alta velocidad. Si ese caché más pequeño falla, se verifica el siguiente caché más rápido, el nivel 2 ( L2 ), y así sucesivamente, antes de acceder a la memoria externa.
A medida que la diferencia de latencia entre la memoria principal y el caché más rápido se ha hecho mayor, algunos procesadores han comenzado a utilizar hasta tres niveles de caché en el chip. Los diseños sensibles al precio utilizaron esto para incorporar toda la jerarquía de caché al chip, pero en la década de 2010 algunos de los diseños de mayor rendimiento volvieron a tener grandes cachés fuera del chip, que a menudo se implementan en eDRAM y se montan en un módulo de múltiples chips. , como cuarto nivel de caché. En casos raros, como en la CPU del mainframe IBM z15 (2019), todos los niveles hasta L1 se implementan mediante eDRAM, reemplazando a SRAM por completo (para el caché, SRAM todavía se usa para los registros). El Apple M1 basado en ARM tiene una caché L1 de 192 KiB para cada uno de los cuatro núcleos de alto rendimiento, una cantidad inusualmente grande; sin embargo, los cuatro núcleos de alta eficiencia sólo tienen 128 KiB.
Los beneficios de las cachés L3 y L4 dependen de los patrones de acceso de la aplicación. Ejemplos de productos que incorporan cachés L3 y L4 incluyen los siguientes:
Finalmente, en el otro extremo de la jerarquía de memoria, el archivo de registros de la CPU en sí puede considerarse el caché más pequeño y más rápido del sistema, con la característica especial de que está programado en software (generalmente mediante un compilador, ya que asigna registros para almacenar). valores recuperados de la memoria principal para, por ejemplo, optimización del nido de bucles . Sin embargo, con el cambio de nombre de los registros, la mayoría de las asignaciones de registros del compilador se reasignan dinámicamente mediante hardware en tiempo de ejecución a un banco de registros, lo que permite a la CPU romper dependencias de datos falsas y, por lo tanto, aliviar los peligros de la canalización.
Los archivos de registro a veces también tienen jerarquía: el Cray-1 (alrededor de 1976) tenía ocho registros de direcciones "A" y ocho registros de datos escalares "S" que generalmente eran utilizables. También había un conjunto de 64 registros de direcciones "B" y 64 registros de datos escalares "T" a los que se tardaba más en acceder, pero que eran más rápidos que la memoria principal. Los registros "B" y "T" se proporcionaron porque el Cray-1 no tenía caché de datos. (Sin embargo, el Cray-1 tenía un caché de instrucciones).
Al considerar un chip con múltiples núcleos , surge la duda de si los cachés deben ser compartidos o locales para cada núcleo. La implementación de caché compartida inevitablemente introduce más cableado y complejidad. Pero entonces, tener un caché por chip , en lugar de núcleo , reduce en gran medida la cantidad de espacio necesario y, por lo tanto, se puede incluir un caché más grande.
Normalmente, compartir la caché L1 no es deseable porque el aumento resultante en la latencia haría que cada núcleo funcione considerablemente más lento que un chip de un solo núcleo. Sin embargo, para el caché de nivel más alto, el último llamado antes de acceder a la memoria, tener un caché global es deseable por varias razones, como permitir que un solo núcleo use todo el caché, reducir la redundancia de datos al permitir diferentes procesos o subprocesos para compartir datos almacenados en caché y reducir la complejidad de los protocolos de coherencia de caché utilizados. [53] Por ejemplo, un chip de ocho núcleos con tres niveles puede incluir una caché L1 para cada núcleo, una caché L2 intermedia para cada par de núcleos y una caché L3 compartida entre todos los núcleos.
Una caché compartida de nivel más alto, a la que se llama antes de acceder a la memoria, generalmente se denomina caché de último nivel (LLC). Se utilizan técnicas adicionales para aumentar el nivel de paralelismo cuando LLC se comparte entre varios núcleos, incluida la división en varias partes que abordan ciertos rangos de direcciones de memoria y a las que se puede acceder de forma independiente. [54]
En una estructura de caché separada, las instrucciones y los datos se almacenan en caché por separado, lo que significa que se utiliza una línea de caché para almacenar en caché instrucciones o datos, pero no ambos; Se han demostrado varios beneficios con buffers de búsqueda de traducción de instrucciones y datos separados . [55] En una estructura unificada, esta restricción no está presente y las líneas de caché se pueden utilizar para almacenar en caché tanto instrucciones como datos.
Los cachés multinivel introducen nuevas decisiones de diseño. Por ejemplo, en algunos procesadores, todos los datos de la caché L1 también deben estar en algún lugar de la caché L2. Estos cachés se denominan estrictamente inclusivos . Otros procesadores (como el AMD Athlon ) tienen cachés exclusivos : se garantiza que los datos estarán como máximo en una de las cachés L1 y L2, nunca en ambas. Otros procesadores (como Intel Pentium II , III y 4 ) no requieren que los datos de la caché L1 también residan en la caché L2, aunque a menudo es posible que así sea. No existe un nombre universalmente aceptado para esta política intermedia; [56] [57] dos nombres comunes son "no exclusivo" y "parcialmente inclusivo".
La ventaja de los cachés exclusivos es que almacenan más datos. Esta ventaja es mayor cuando la caché L1 exclusiva es comparable a la caché L2 y disminuye si la caché L2 es muchas veces mayor que la caché L1. Cuando la L1 falla y la L2 acierta en un acceso, la línea de caché de acierto en la L2 se intercambia con una línea en la L1. Este intercambio requiere bastante más trabajo que simplemente copiar una línea de L2 a L1, que es lo que hace un caché inclusivo. [57]
Una ventaja de los cachés estrictamente inclusivos es que cuando dispositivos externos u otros procesadores en un sistema multiprocesador desean eliminar una línea de caché del procesador, sólo necesitan que el procesador verifique el caché L2. En las jerarquías de caché que no exigen la inclusión, también se debe verificar la caché L1. Como inconveniente, existe una correlación entre las asociatividades de las cachés L1 y L2: si la caché L2 no tiene al menos tantas formas como todas las cachés L1 juntas, la asociatividad efectiva de las cachés L1 está restringida. Otra desventaja del caché inclusivo es que siempre que hay un desalojo en el caché L2, las líneas (posiblemente) correspondientes en L1 también deben ser desalojadas para mantener la inclusión. Esto supone bastante trabajo y daría como resultado una mayor tasa de fallos en L1. [57]
Otra ventaja de las cachés inclusivas es que la caché más grande puede utilizar líneas de caché más grandes, lo que reduce el tamaño de las etiquetas de caché secundarias. (Los cachés exclusivos requieren que ambos cachés tengan líneas de caché del mismo tamaño, de modo que las líneas de caché se puedan intercambiar en caso de error L1 o acierto L2). Si el caché secundario es un orden de magnitud mayor que el primario y los datos del caché son un Un orden de magnitud mayor que las etiquetas de caché, el área de esta etiqueta guardada puede ser comparable al área incremental necesaria para almacenar los datos de la caché L1 en la L2. [58]
La memoria scratchpad (SPM), también conocida como scratchpad, scratchpad RAM o almacén local en terminología informática, es una memoria interna de alta velocidad que se utiliza para el almacenamiento temporal de cálculos, datos y otros trabajos en curso.
Para ilustrar tanto la especialización como el almacenamiento en caché multinivel, aquí se muestra la jerarquía de caché del núcleo K8 en la CPU AMD Athlon 64 . [59]
El K8 tiene cuatro cachés especializados: un caché de instrucciones, un TLB de instrucciones , un TLB de datos y un caché de datos. Cada uno de estos cachés está especializado:
El K8 también tiene cachés de múltiples niveles. Hay TLB de datos e instrucciones de segundo nivel, que almacenan solo PTE que asignan 4 KiB. Tanto las cachés de instrucciones como de datos, y los diversos TLB, se pueden llenar desde la gran caché L2 unificada . Este caché es exclusivo tanto para el caché de instrucciones como para el caché de datos L1, lo que significa que cualquier línea de 8 bytes solo puede estar en uno de los cachés de instrucciones L1, caché de datos L1 o caché L2. Sin embargo, es posible que una línea en el caché de datos tenga un PTE que también esté en uno de los TLB; el sistema operativo es responsable de mantener los TLB coherentes al vaciar partes de ellos cuando se actualizan las tablas de páginas en la memoria.
El K8 también almacena en caché información que nunca se almacena en la memoria: información de predicción. Estos cachés no se muestran en el diagrama anterior. Como es habitual en esta clase de CPU, el K8 tiene una predicción de bifurcaciones bastante compleja , con tablas que ayudan a predecir si se toman bifurcaciones y otras tablas que predicen los objetivos de las bifurcaciones y los saltos. Parte de esta información está asociada con instrucciones, tanto en la caché de instrucciones de nivel 1 como en la caché secundaria unificada.
El K8 utiliza un truco interesante para almacenar información de predicción con instrucciones en el caché secundario. Las líneas en el caché secundario están protegidas contra la corrupción accidental de datos (por ejemplo, por el impacto de una partícula alfa ) mediante ECC o paridad , dependiendo de si esas líneas fueron expulsadas de los cachés primarios de datos o de instrucciones. Dado que el código de paridad ocupa menos bits que el código ECC, las líneas del caché de instrucciones tienen algunos bits de repuesto. Estos bits se utilizan para almacenar en caché la información de predicción de rama asociada con esas instrucciones. El resultado neto es que el predictor de sucursales tiene una tabla de historial efectivo más grande y, por lo tanto, tiene mayor precisión.
Otros procesadores tienen otros tipos de predictores (por ejemplo, el predictor de derivación de almacenamiento a carga en el DEC Alpha 21264 ), y es probable que florezcan varios predictores especializados en procesadores futuros.
Estos predictores son cachés porque almacenan información que es costosa de calcular. Parte de la terminología utilizada cuando se habla de predictores es la misma que para las cachés (se habla de un acierto en un predictor de rama), pero los predictores generalmente no se consideran parte de la jerarquía de caché.
El K8 mantiene las cachés de instrucciones y datos coherentes en el hardware, lo que significa que un almacenamiento en una instrucción que sigue de cerca a la instrucción de almacenamiento cambiará la siguiente instrucción. Otros procesadores, como los de la familia Alpha y MIPS, han dependido del software para mantener coherente la caché de instrucciones. No se garantiza que las tiendas aparezcan en el flujo de instrucciones hasta que un programa llame a una instalación del sistema operativo para garantizar la coherencia.
En ingeniería informática, se utiliza una etiqueta RAM para especificar cuál de las posibles ubicaciones de memoria está almacenada actualmente en un caché de la CPU. [60] [61] Para un diseño simple y de mapeo directo, se puede usar SRAM rápida. Las cachés asociativas superiores suelen emplear memoria direccionable por contenido .
Las lecturas de caché son la operación de CPU más común que requiere más de un ciclo. El tiempo de ejecución del programa tiende a ser muy sensible a la latencia de un acierto en la caché de datos de nivel 1. Se gasta una gran cantidad de esfuerzo de diseño y, a menudo, energía y área de silicio para hacer que las cachés sean lo más rápidas posible.
El caché más simple es un caché de mapeo directo virtualmente indexado. La dirección virtual se calcula con un sumador, la parte relevante de la dirección se extrae y se utiliza para indexar una SRAM, que devuelve los datos cargados. Los datos se alinean en bytes en un desplazador de bytes y desde allí se pasan a la siguiente operación. No es necesario verificar ninguna etiqueta en el bucle interno; de hecho, ni siquiera es necesario leer las etiquetas. Más adelante en el proceso, pero antes de que se retire la instrucción de carga, se debe leer la etiqueta de los datos cargados y compararla con la dirección virtual para asegurarse de que haya un acierto en la caché. En caso de error, la caché se actualiza con la línea de caché solicitada y se reinicia la canalización.
Una caché asociativa es más complicada porque se debe leer algún tipo de etiqueta para determinar qué entrada de la caché seleccionar. Una caché de nivel 1 de conjunto asociativo de N vías generalmente lee todas las N etiquetas posibles y N datos en paralelo, y luego elige los datos asociados con la etiqueta coincidente. Las cachés de nivel 2 a veces ahorran energía al leer primero las etiquetas, de modo que solo se lee un elemento de datos de la SRAM de datos.
El diagrama adjunto pretende aclarar la manera en que se utilizan los distintos campos de la dirección. El bit de dirección 31 es el más significativo, el bit 0 es el menos significativo. El diagrama muestra las SRAM, la indexación y la multiplexación para una caché de 4 KiB, asociativa por conjuntos de 2 vías, virtualmente indexada y virtualmente etiquetada con líneas de 64 bytes (B), un ancho de lectura de 32 bits y una dirección virtual de 32 bits.
Debido a que el caché es de 4 KiB y tiene 64 líneas B, solo hay 64 líneas en el caché y leemos dos a la vez desde una Tag SRAM que tiene 32 filas, cada una con un par de etiquetas de 21 bits. Aunque se podría utilizar cualquier función de los bits de dirección virtual 31 a 6 para indexar las etiquetas y las SRAM de datos, lo más sencillo es utilizar los bits menos significativos.
De manera similar, debido a que el caché es de 4 KiB y tiene una ruta de lectura de 4 B, y lee de dos maneras para cada acceso, la SRAM de datos tiene 512 filas por 8 bytes de ancho.
Una caché más moderna podría ser de 16 KiB, asociativa por conjuntos de 4 vías, virtualmente indexada, virtualmente insinuada y etiquetada físicamente, con 32 líneas B, ancho de lectura de 32 bits y direcciones físicas de 36 bits. La recurrencia de la ruta de lectura para dicho caché es muy similar a la ruta anterior. En lugar de etiquetas, se leen vhints y se comparan con un subconjunto de la dirección virtual. Más adelante en la canalización, el TLB traduce la dirección virtual a una dirección física y se lee la etiqueta física (solo una, ya que vhint indica en qué dirección del caché leer). Finalmente, la dirección física se compara con la etiqueta física para determinar si se ha producido un acierto.
Algunos diseños SPARC han mejorado la velocidad de sus cachés L1 mediante algunos retrasos en la puerta al colapsar el sumador de direcciones virtuales en los decodificadores SRAM. Véase decodificador de direcciones sumadas .
La historia temprana de la tecnología de caché está estrechamente ligada a la invención y el uso de la memoria virtual. [ cita necesaria ] Debido a la escasez y el costo de las memorias semiconductoras, las primeras computadoras centrales de la década de 1960 usaban una jerarquía compleja de memoria física, asignada a un espacio de memoria virtual plano utilizado por los programas. Las tecnologías de memoria abarcarían semiconductores, núcleos magnéticos, tambores y discos. La memoria virtual vista y utilizada por los programas sería plana y el almacenamiento en caché se utilizaría para recuperar datos e instrucciones en la memoria más rápida antes del acceso al procesador. Se realizaron extensos estudios para optimizar los tamaños de caché. Se descubrió que los valores óptimos dependían en gran medida del lenguaje de programación utilizado: Algol necesitaba el tamaño de caché más pequeño y Fortran y Cobol necesitaban los tamaños de caché más grandes. [ disputado – discutir ]
En los primeros días de la tecnología de las microcomputadoras, el acceso a la memoria era sólo un poco más lento que el acceso a los registros . Pero desde la década de 1980 [62] la brecha de rendimiento entre el procesador y la memoria ha ido creciendo. Los microprocesadores han avanzado mucho más rápido que la memoria, especialmente en cuanto a su frecuencia de funcionamiento , por lo que la memoria se convirtió en un cuello de botella en el rendimiento . Si bien era técnicamente posible tener toda la memoria principal tan rápida como la CPU, se ha tomado un camino más viable económicamente: utilizar mucha memoria de baja velocidad, pero también introducir una pequeña memoria caché de alta velocidad para aliviar la brecha de rendimiento. Esto proporcionó un orden de magnitud más de capacidad (por el mismo precio) con sólo un rendimiento combinado ligeramente reducido.
Los primeros usos documentados de un TLB fueron en el GE 645 [63] y el IBM 360/67 , [64] los cuales usaban una memoria asociativa como TLB.
El primer uso documentado de un caché de instrucciones fue en el CDC 6600 . [65]
El primer uso documentado de un caché de datos fue en IBM System/360 Modelo 85. [66]
El 68010 , lanzado en 1982, tiene un "modo de bucle" que puede considerarse un caché de instrucciones pequeño y de caso especial que acelera bucles que constan de sólo dos instrucciones. El 68020 , lanzado en 1984, lo reemplazó con un caché de instrucciones típico de 256 bytes, siendo el primer procesador de la serie 68k que presenta una verdadera memoria caché en el chip.
El 68030 , lanzado en 1987, es básicamente un núcleo 68020 con un caché de datos adicional de 256 bytes, una unidad de administración de memoria (MMU) en el chip , una reducción de proceso y un modo de ráfaga agregado para los cachés.
El 68040 , lanzado en 1990, tiene cachés de datos e instrucciones divididos de cuatro kilobytes cada uno.
El 68060 , lanzado en 1994, tiene lo siguiente: caché de datos de 8 KiB (asociativo de cuatro vías), caché de instrucciones de 8 KiB (asociativo de cuatro vías), búfer de instrucciones FIFO de 96 bytes, caché de rama de 256 entradas y caché de rama de 64 entradas. Búfer MMU de caché de traducción de direcciones (asociativo de cuatro vías).
A medida que los microprocesadores x86 alcanzaron velocidades de reloj de 20 MHz y superiores en el 386 , comenzaron a incluirse en los sistemas pequeñas cantidades de memoria caché rápida para mejorar el rendimiento. Esto se debía a que la DRAM utilizada para la memoria principal tenía una latencia significativa, de hasta 120 ns, así como ciclos de actualización. La caché se construyó a partir de celdas de memoria SRAM más caras, pero significativamente más rápidas , que en ese momento tenían latencias de entre 10 y 25 ns. Los primeros cachés eran externos al procesador y generalmente se ubicaban en la placa base en forma de ocho o nueve dispositivos DIP colocados en zócalos para habilitar el caché como una característica adicional opcional o de actualización.
Algunas versiones del procesador Intel 386 podrían admitir de 16 a 256 KiB de caché externo.
Con el procesador 486 , se integró una caché de 8 KiB directamente en la CPU. Este caché se denominó caché de nivel 1 o L1 para diferenciarlo del caché más lento de la placa base o caché de nivel 2 (L2). Estos cachés en la placa base eran mucho más grandes, siendo el tamaño más común de 256 KiB. Había algunas placas de sistema que contenían zócalos para la tarjeta secundaria Intel 485Turbocache que tenía 64 o 128 Kbytes de memoria caché. [67] [68] La popularidad del caché en la placa base continuó durante la era Pentium MMX , pero quedó obsoleto con la introducción de SDRAM y la creciente disparidad entre las velocidades de reloj del bus y las velocidades de reloj de la CPU, lo que provocó que el caché en la placa base fuera solo ligeramente más rápido que la memoria principal.
El siguiente desarrollo en la implementación de la caché en los microprocesadores x86 comenzó con el Pentium Pro , que llevó la caché secundaria al mismo paquete que el microprocesador, con la misma frecuencia que el microprocesador.
Los cachés en la placa base gozaron de una popularidad prolongada gracias a los procesadores AMD K6-2 y AMD K6-III que todavía usaban el Socket 7 , que Intel usaba anteriormente con los cachés en la placa base. K6-III incluía 256 KiB de caché L2 integrada y aprovechó la caché integrada como caché de tercer nivel, denominada L3 (se produjeron placas base con hasta 2 MiB de caché integrada). Después de que el Socket 7 quedó obsoleto, el caché de la placa base desapareció de los sistemas x86.
Las cachés de tres niveles se volvieron a utilizar primero con la introducción de múltiples núcleos de procesador, donde la caché L3 se agregó a la CPU. Se volvió común que los tamaños totales de caché fueran cada vez mayores en las nuevas generaciones de procesadores y recientemente (a partir de 2011) no es raro encontrar tamaños de caché de nivel 3 de decenas de megabytes. [69]
Intel introdujo un caché en paquete de nivel 4 con la microarquitectura Haswell . Crystalwell [38] Las CPU Haswell, equipadas con la variante GT3e de los gráficos integrados Iris Pro de Intel, cuentan efectivamente con 128 MiB de DRAM integrada ( eDRAM ) en el mismo paquete. Esta caché L4 se comparte dinámicamente entre la GPU integrada y la CPU, y sirve como caché de víctima para la caché L3 de la CPU. [39]
La CPU Apple M1 tiene caché L1 de instrucciones de 128 o 192 KiB para cada núcleo (importante para la latencia/rendimiento de un solo subproceso), según el tipo de núcleo. Se trata de una caché L1 inusualmente grande para cualquier tipo de CPU (no sólo para una computadora portátil); El tamaño total de la memoria caché no es inusualmente grande (el total es más importante para el rendimiento) para una computadora portátil, y en los mainframes de IBM hay disponibles tamaños totales mucho mayores (por ejemplo, L3 o L4).
Los primeros diseños de caché se centraron completamente en el costo directo del caché y la RAM y la velocidad promedio de ejecución. Los diseños de caché más recientes también consideran la eficiencia energética , la tolerancia a fallos y otros objetivos. [70] [71]
Hay varias herramientas disponibles para los arquitectos informáticos para ayudar a explorar las compensaciones entre el tiempo, la energía y el área del ciclo de caché; el simulador de caché CACTI [72] y el simulador de conjunto de instrucciones SimpleScalar son dos opciones de código abierto.
Una caché multipuerto es una caché que puede atender más de una solicitud a la vez. Cuando accedemos a una caché tradicional normalmente utilizamos una única dirección de memoria, mientras que en una caché multipuerto podemos solicitar N direcciones a la vez, donde N es el número de puertos que se conectaron a través del procesador y la caché. El beneficio de esto es que un procesador canalizado puede acceder a la memoria desde diferentes fases de su canalización. Otro beneficio es que permite el concepto de procesadores superescalares a través de diferentes niveles de caché.
En Cambridge se desarrollaron dos almacenes de diodos de túnel; uno, que funcionó muy bien, aceleró la búsqueda de operandos, el otro estaba destinado a acelerar la búsqueda de instrucciones. La idea era que la mayoría de las instrucciones se obedecieran en secuencia, de modo que cuando se recuperaba una instrucción, esa palabra se colocaba en el almacén de esclavos en la ubicación dada por la dirección de recuperación módulo 32; También se almacenaron los bits restantes de la dirección de recuperación. Si la palabra buscada estaba en el esclavo, se leía desde allí en lugar de desde la memoria principal. Esto daría una gran aceleración a los bucles de instrucciones de hasta 32 instrucciones de longitud y un efecto reducido para bucles de hasta 64 palabras.
Caché L1 de 32 KB/núcleo, caché L2 de 4,5 MB por clúster de 4 núcleos y caché LLC compartido de hasta 15 MB.
Se ha demostrado que las cachés asociativas sesgadas tienen dos ventajas principales sobre las cachés asociativas de conjuntos convencionales.