Coherencia de caché, así se coordinan los núcleos de la CPU

Coherencia de caché, así se coordinan los núcleos de la CPU

Josep Roca

Si habéis visto cualquier diagrama de cualquier CPU multinúcleo, independientemente del diseñador, fabricante e incluso el tipo de sistema siempre os habréis encontrado con el diagrama:

CPU-Multinúcleo

El motivo de ello tiene que ver con lo que llamamos coherencia de cache.

Para entender el concepto tenemos que imaginarnos lo siguiente:  Vamos a suponer que hay varias personas personas cada una delante de una terminal , todas ellas editando el mismo archivo que se encuentra en el servidor. Si una de estas personas cambia una parte del documento entonces 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. Pero un día ese sistema falla y de repente todo se descordina, 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 realicen sobre el documento será independiente al que hagan otros.

Aplicad esto a un programa informático y entonces tenemos el caos absoluto, donde un pequeño cambio realizado por un núcleo en la memoria RAM del que el resto no sean conscientes puede suponer un fallo critico en el sistema o la ejecución de datos erroneos.

Coherencia de caches y jerarquía

Las caches almacena una copia de una parte de la memoria RAM, la más cercana al código que se esta ejecutando en este momento por la CPU, por lo que las caches no tienen una versión actualizada del contenido de la memoria sino una copia. Pensad en ella como las pantallas de las terminales del símil del que os hemos hablado más arriba.

Pero en los sistemas multinúcleo se añade un nivel de cache adicional, la cual se encuentra entre el controlador de memoria y los niveles anteriores de la cache, a la cual se conectan todos ellos.

Esto se hace así por el hecho que hacer que varios núcleos accedan a la memoria RAM al mismo tiempo supondría una serie de problemas y conflictos, utilizando una cache global en el último nivel tiene consecuencia que solo es necesario actualizar la linea en esa cache para que las caches más cercanas al procesador se actualicen, ya que estas tienen copias de secciones de las caches en niveles posteriores.

por lo que los núcleos acceden de uno a uno a la memoria y se utilizan las caches para reducir la cantidad de accesos de los mismos. Claro esta que si hay una copia de una linea de la memoria RAM en las caches y esta es modificada, entonces tiene que haber un mecanismo que actualice las copias de esa linea de memoria en el resto de núcleos del procesador e incluso en la propia memoria.

Si no existiese la cache compartida de último nivel, entonces cada uno de los núcleos a la hora de comprobar la coherencia del contenido de sus caches con la RAM tendrían que acceder a la RAM varias veces de manera continuada, con la cache compartida de último nivel se evita este problema en parte ya que pese a que no todo el contenido de las caches de nivel más alejado al procesador esta en las caches más cercanas al procesador, las que están en los niveles más alejados al procesador si que guardan copia de las que están más cercanas al mismo.

Por lo que de cara a mantener la coherencia de cache, es mucho más económico de cara al diseño no cambiar todas las líneas de las diferentes caches privadas de cada núcleo sino que actualizando la cache de último nivel se actualizan el resto de caches más cercanas al procesador.

El proceso de actualización de las caches

¿Pero que ocurre si dos núcleos quieren acceder al mismo dato? Aquí es donde aparecen dos métodos.

  • El primer método es invalidar todas las escrituras a las copias de la misma línea de memoria en el resto de caches excepto la del núcleo que la esta utilizando en este momento.
  • El segundo método es hacer que cuando un núcleo modifique una línea de la cache entonces se modifiquen también las copias de esa misma línea en el resto de caches de la CPU.

s las caches entre si, el segundo bit indica si el contenido de esa linea de cache y el de la de la memoria RAM de la que es supuestamente copia es el mismo.

Así pues, cuando el primer núcleo escribe sobre su línea de cache y modifica el contenido de la línea entonces marca el bit de coherencia con la memoria, cuando ocurre eso toda línea de cache que apunta a esa dirección de memoria es marcada como «reservada», a continuación el contenido de la línea de memoria a la que apuntan todas esas caches es modificado con el nuevo contenido.

Coherencia de caches y jerarquía

Jerarquia Caches

Como comprobar todos los niveles de cache de la CPU (o GPU) con la memoria sería un trabajo titánico que haría sumamente lento ay complejo al procesador, la comprobación entre la coherencia del contenido de la cache y la RAM se hace entre la cache de último nivel de CPU (o GPU) y la memoria. Esto se hace así porque las caches de niveles anteriores y por tanto más cercanas al procesador no están conectadas directamente a la RAM sino al siguiente nivel de cache, de tal manera que la coherencia del contenido no se comprueba respecto a la RAM sino respecto al siguiente nivel de la cache.

Tened que el contenido de los diferentes niveles de la cache de un procesador es como la de una muñeca rusa. Si por ejemplo tenemos una cache de tres niveles, entonces el tercer nivel tendrá el contenido suyo y de los dos primeros niveles, el segundo nivel tendrá el contenido suyo y el primer nivel solo el suyo propio. Por eso el sistema de coherencia entre cache y memoria solo necesita cambiar el último nivel de la cache, porque cualquier cambio en este será un cambio en los niveles anteriores.