Cuando adquirimos un procesador nos solemos fijar en la cantidad de núcleos y de hilos, así como sus frecuencias. Hay muchos otros parámetros que deberíamos revisar o en los que nos deberíamos fijar. Uno de los más importantes dentro de una CPU y, también de una GPU, es la memoria caché. Pero, ¿sabes para qué sirve la memoria caché?
Es posiblemente uno de los parámetros más olvidados a la hora de elegir componentes. Aunque siempre se marca en los procesadores, no mucha gente hace caso a este valor que afecta directamente al rendimiento. Por otro lado, no se suele indicar en las tarjetas gráficas, aunque estas cuentan con este tipo de memoria.
Debido a su importancia, creemos que os debemos explicar sus caracteristicas y su papel en la CPU y la GPU, entre otros. Haremos una profundización en la memoria caché, que también te servirá para ver la diferencia que existe con respecto la memoria RAM.
¿Qué es la memoria caché?
La memoria caché se trata de un tipo de memoria de muy pequeño tamaño que se encuentra a día de hoy dentro del procesador. Su trabajo es almacenar una copia de la memoria RAM para que sea más rápido acceder a los datos y, por tanto, dar más rendimiento al procesador. Dado que gracias a la inclusión de esta memoria las instrucciones tienen más posibilidades a ejecutarse en mucho menos tiempo.
Su implementación en los ordenadores domésticos se vio por primera vez en el Intel 80486. Aunque sus orígenes se remontan al IBM S/360, donde la idea fue implementada de la memoria caché fue implementada por primera vez.
Actualmente, es un elemento muy importante en las CPU y las GPU. Tiene gran importancia en el rendimiento de estos componentes. Debes saber que sus características y funcionamiento difiere notablemente de la memoria RAM. La caché tiene una serie de complejidades en su estructura y funcionamiento.
¿Por qué es necesaria la memoria caché?
La memoria caché es necesaria por el hecho que la memoria RAM es demasiado lenta como para que una CPU pueda ejecutar sus instrucciones con la suficiente velocidad y no podemos hacer que vaya más rápida. Esto se debe a que la velocidad de los procesadores depende también de la comunicación con la RAM y estos han evolucionado mucho más rápido que la memoria principal. Tanto es así, que estamos hablando de una media del 60%, por un lado, y solo de un 7% por otro.
¿La solución? Añadir un pozo de memoria interno en el procesador que permite acercar los datos e instrucciones más cercanos al bloque del programa que está ejecutando el procesador. Claro está, que esto forzaría a los programas a tener que realizar todo el proceso de copia de datos de manera manual. ¿Las consecuencias? Ruptura de la compatibilidad hacia atrás. Por lo que la memoria caché no solo es ídem en sí misma, sino que tiene que venir con una serie de mecanismos que copien la información desde la RAM de manera automática.
Debido a que la caché está dentro del procesador, una vez la CPU encuentra los datos en su interior, los ejecuta mucho más rápido que si tuviese un acceso natural a la memoria RAM. Esto se debe a que la distancia ha de atravesar la señal eléctrica es menor, reduciendo así al tiempo y el consumo. En este caso es debido a que el cableado es más corto y, por tanto, hay menos resistencia.
¿Cómo funciona la memoria caché?
Hemos de partir de la base de que la memoria caché no se considera memoria RAM ni funciona como tal. Es más, debido a su naturaleza, los programas no pueden ocupar ni liberar memoria cuando se les antoje. ¿El motivo? La caché funciona totalmente aparte de la memoria RAM y es gestionada automáticamente. Por lo que ninguna aplicación hará acceso explícito a dicha memoria.
Entender su funcionamiento es sencillo. Cuando el núcleo de un procesador está ejecutando un programa, lo que hace es mirar de manera secuencial la memoria RAM. Es decir que, si la instrucción actual se encuentra en la línea 1000, entonces la siguiente se encontrará en la 1001. Puede que haya una instrucción de salto. En general lo que se hace es copiar el contenido de las direcciones de RAM colindantes a la caché con el nivel más alto dentro del procesador.
La jerarquía de memoria
La memoria caché forma parte de la jerarquía de memoria. Cuando el procesador ha de ejecutar una instrucción y su dato no se encuentra en los registros entonces recorrerá todas las cachés desde el primer nivel hasta el último para encontrar dicho dato. Si no lo encuentra en la memoria caché, entonces significará que dicha información se encuentra en la RAM del sistema.
Hay unas reglas de la jerarquía de memoria son muy claras, empieza desde los registros del procesador y termina en la memoria más lenta del mismo y sigue siempre las mismas normas:
- El nivel actual de la jerarquía tiene más capacidad que el anterior, pero menos que el siguiente
- A medida que nos vamos alejando de la CPU va aumentando la latencia de las instrucciones
- A medida que nos vamos alejando de la CPU va disminuyendo el ancho de banda con los datos
En el caso concreto de los niveles de caché, los niveles más bajos son de acceso más rápido, ya que son los que primero se comprueban. Esto es gracias a que físicamente están más cercanas a las unidades de ejecución del procesador. Así pues, la caché L1 es un subconjunto de los datos de la caché L2 que a su vez es un superconjunto de los datos de la caché L1 y un subconjunto de los datos de la caché L3 si la hubiera. De este modo hasta llegar a la caché de último nivel que, como os hemos comentado antes, guarda la porción de la RAM actual a donde apunta el procesador.
Niveles de caché en CPU y GPU
El motivo por el cual existen varios niveles de caché es para evitar la contención en el acceso a la información. Entendemos como contención cuando varios elementos quieren acceder a una memoria al mismo tiempo y esta no puede suplirla simultáneamente. Por lo que parte de los clientes tienen que esperar ciclos de reloj adicionales. Es por ello que en sistemas con varios núcleos o procesadores en un solo chip hay por lo menos dos niveles de caché.
Esto ocurre en aquellos que comparten la misma memoria RAM. Por lo que hay una sola interfaz por esta y, por tanto, varios procesadores peleándose por un acceso a la misma. Por lo que se hace necesario crear un nivel de caché adicional. Uno común para todos los procesadores que ha de ser coherente y otro privado para cada núcleo. La cual se encuentra a un nivel superior en la jerarquía de memoria.
La caché de primer nivel
La caché L1 o caché de primer nivel es la más cercana a los núcleos de la CPU y la GPU, por lo que es el tipo de caché con mayor ancho de banda y menor latencia de toda la jerarquía de cachés. Es la primera en la que, a la hora de buscar datos en cualquier tipo de procesador, el sistema de jerarquía de memoria va a mirar para buscar los datos. La caché L1 en todos los procesadores está dividida en dos pozos distintos, una caché de datos y una de instrucciones. ¿Qué significa esto? Es sencillo, primero hemos de entender que todas las CPU y GPU utilizan una arquitectura Von Neumann donde las instrucciones que son lo que el procesador ha de hacer, y los datos que son lo que ha de manipular la instrucción, se encuentran en la misma memoria RAM.
Esto se traduce en que las instrucciones tienen una forma en la que x bits del código que forman una instrucción, los primeros bits corresponden el opcode que indican la acción que ha de hacer el procesador, bits que vienen a continuación corresponden a como se a hacer la instrucción y los últimos hacen referencia al dato. Ya sea donde se encuentra el dato o esté en sí mismo, aunque hay instrucciones que carecen de los bits de datos y otros de los bits de modo. El motivo de ello es que a nivel interno lo que es la información del opcode de la instrucción se trata diferente al resto de los datos. Cuando una línea de caché con un dato y su instrucción llegan a la caché de primer nivel, este es separado por el decodificador de la unidad de control del procesador.
Niveles de caché adicionales
En el diseño de cualquier procesador se pueden añadir varios niveles adicionales al procesador. Aunque en un sistema multinúcleo siempre habrá dos, por un lado, la caché de primer nivel dividida en dos pozos distintos y en el último nivel una caché global que engloba todos los elementos de un procesador. Sin embargo, no todos los elementos de un sistema tienen acceso al último nivel de caché o ni tan siquiera a ningún nivel. A estos los llamamos los elementos no coherentes del procesador.
Los niveles adicionales de memoria caché pueden ser de ámbito privado o semi-privado. Por ejemplo, podemos tener un procesador de 8 núcleos dividido en 2 clústeres o conjuntos de 4. Cada núcleo tendría su caché L1 de datos e instrucciones. Habría una caché L2 para cada conjunto y luego una L3 como caché final para agrupar a todos los procesadores.
Rendimiento de la memoria caché en CPU y GPU
Una vez que hemos hecho ya la introducción de sus funciones básicas, hemos de entender cómo funciona el rendimiento de la memoria caché. Aunque más bien deberíamos hablar en como influencia al del procesador.
- Cuando la CPU o la GPU necesita acceder a un dato que se encuentra en memoria, lo primero que hará será acceder a los diferentes niveles de la caché previos.
- Primero buscará en la caché de primer nivel,
- Si no encuentra el dato, entonces bajará al siguiente nivel y así hasta encontrar el dato que se está buscando.
El sistema no salta repentinamente de un nivel de caché a otro en tiempo cero, sino que existe un tiempo de desfase, al final todo se resume en una fórmula muy sencilla:
Tiempo de búsqueda del dato en caché = tiempo de búsqueda en la caché en «n» niveles + tiempo de desfase por salto en «n-1» niveles.
Los tiempos de desfase suman a la latencia de acceso y cada nuevo nivel de caché es más grande que el anterior, por lo que el procesador tardará en recorrerlo. En el caso de que una CPU o GPU tarde más tiempo en recorrer sus cachés que la latencia con la RAM, entonces estaremos ante una mala implementación. Ya que bajo ningún sentido lógico tiene justificación que se tarde más en buscar un dato en la caché que en la memoria. Es por eso no solemos ver niveles de caché adicionales, puesto que los diferentes tiempos de desfase añadidos por cada nivel añaden aún más latencia adicional al tiempo de acceso.
Midiendo el AMAT
La explicación que os hemos dado en la sección anterior es más que nada introductoria. Bajo las siglas AMAT nos referimos Average Memory Access Time, o tiempo medio de acceso a memoria. Es una media por el hecho que no todas las instrucciones, una CPU o una GPU tienen la misma latencia y dependen de la memoria de la misma manera. Pero al mismo tiempo nos sirve para medir el rendimiento de la caché de una CPU o una GPU.
¿Cómo se calcula?
Para calcular el AMAT de cualquier núcleo CPU o GPU se utiliza la siguiente fórmula:
AMAT = (Hit Time + Miss Rate) * Miss Penalty
- El primer valor que es el Hit Time, que es el tiempo en que la CPU o la GPU tardará en encontrar el dato en la caché.
- El segundo valor es el Miss Rate, que es el porcentaje de veces en los que los datos no se encuentran en la caché.
- El tercer valor es el Miss Penalty, este es el tiempo de latencia que se tarda en acceder a un dato si se encuentra en la memoria RAM y no en la caché. Esto supone un tiempo enorme en lo que a ciclos de reloj se refiere
Desgraciadamente, lo ideal sería beneficiar los tres valores, pero es imposible. Por ejemplo, si queremos que los datos se encuentren rápido necesitamos cachés muy cortas, sin embargo, eso disminuye la posibilidad de encontrarlos. Para terminar, se ha de tener en cuenta que si los datos no se encuentran en la memoria caché, entonces se tendrán que buscar en la RAM. Lo que significa un aumento en el tiempo de ejecución, escenario que se puede dar, pero que no es habitual.
La relación con los niveles de caché
Uno de los problemas de rendimiento más grandes es cuando se produce un Caché Miss que ocurre cuando un dato no se encuentra en un nivel de caché. Eso es sumamente peligroso para el rendimiento de una CPU en-orden, ya que la consecuencia son una gran cantidad de ciclos del procesador perdidos, pero no por ello es menos peligroso en una CPU con ejecución fuera de orden.
Para el diseño de una CPU, el hecho que la suma total del tiempo de búsqueda de todos los Caché Miss combinados junto al tiempo de búsqueda sea superior a buscar los datos directamente en la caché es un fracaso. Muchos diseños de CPU han tenido que volver a la mesa de diseño por el hecho que el tiempo de búsqueda es superior al acceso a la RAM.
Es por eso que los arquitectos son muy reacios a añadir niveles adicionales en una arquitectura porque sí, tiene que estar justificado de cara a una mejora del rendimiento.
¿Qué es la coherencia de caché?
La coherencia de caché es uno de los elementos que diferencian este tipo de memoria de la RAM y es un punto importante en su funcionamiento. Entendemos como coherencia entre procesadores el hecho de que todos ellos vean el mismo contenido en la misma porción de memoria. Por lo que es una parte esencial en cualquier procesador multinúcleo.
Para entender el concepto tenemos que imaginarnos la siguiente situación:
- Vamos a suponer que hay varias personas cada una delante de una terminal.
- Todas ellas se encuentran editando el mismo archivo que se encuentra en el servidor.
- Si una de estas personas cambia una parte del documento, entonces se refleja en la pantalla del resto de los editores.
Pero para ello hace falta un sistema que lo que haga que cada vez que se realice un cambio en el documento transmita la información al resto de pantallas de tal manera que el cambio se pueda ver a tiempo real. Por otra parte, un día ese sistema falla y de repente todo queda descoordinado, pese a que sus ojos están editando el mismo documento, en realidad cada una de las personas tiene su versión del mismo y cualquier cambio que efectúen sobre el documento será independiente al que hagan otros, lo cual puede acabar siendo un desastre.
Aplicad esto a un programa informático y entonces tenemos el caos absoluto. Donde un pequeño cambio ejecutado por un núcleo en la memoria RAM del que el resto no sean conscientes puede suponer un fallo crítico en el sistema o la ejecución de datos erróneos.
Los mecanismos para la coherencia
El hecho de tener que comprobar todas las líneas de la caché de una CPU (o GPU) con el contenido de la memoria RAM sería un trabajo titánico que haría sumamente lento y complejo al procesado. Aunque es necesario con tal de que todos los elementos reciban la información correcta a cada momento. Por lo que existen una serie de mecanismos de coherencia. El primero de ellos son una serie de bits a cada línea de caché que le marcaran al procesador si el contenido de la misma puede ser utilizado.
- Inválida/Válida: dependiendo del valor de este bit se sabe si se ha realizado una comprobación del contenido de dicha línea de caché.
- Reservada: esta parte de la caché está siendo accedida por uno de los procesadores, por lo que se la protege de accesos de terceros.
- Sucia: una línea de caché que esta sucia es aquella sobre la que no ha dado tiempo a efectuar el proceso de verificación de la coherencia de caché, pero tiene la versión más actualizada generada por el procesador. Esto lo que provocará es una actualización de todas las líneas de caché y de la información de la RAM.
Así pues, gracias a esto, cualquier cambio que se haga sobre una copia de la RAM en la caché en un nivel se reflejará también en el resto de niveles y en la memoria del sistema. Para que la información esté siempre actualizada a tiempo real. Trabajo que efectúan de forma automática y de forma transparente una serie de unidades especializadas para esta tarea.