Skip to content Skip to navigation

OpenStax_CNX

You are here: Home » Content » Caches

Navigation

Recently Viewed

This feature requires Javascript to be enabled.
 

Si, partiendo de los registros, descendemos en la jerarquía de memoria, encontramos las caches. Se trata de pequeñas cantidades de SRAM que almacenan un subconjunto de lo contenidos de la memoria. La esperanza es que la cache tenga el subconjunto adecuado de memoria principal en el momento adecuado.

La arquitectura de la caché tuvo que cambiar conforme la duración del ciclo de los procesadores ha mejorado. Los procesadores son tan rápidos que ni siquiera los chips de SRAM son lo suficientemente rápidos. Ello ha conducido a un enfoque de cache multinivel con uno, o incluso dos, niveles de la misma implementadas como parte del procesador.Table 1 muestra la velocidad aproximada para acceder a la jerarquía de memoria de una DEC Alpha 21164 a 500 MHz.

Table 1: Velocidades de acceso a la memoria en una DEC Alpha 21164.
Registros 2 ns
Nivel 1 en el chip 4 ns
Nivel 2 en el chip 5 ns
Nivel 3 fuera del chip 30 ns
Memoria 220 ns

Cuando puede encontrarse en una cache cada uno de los datos referenciados, se dice que se tiene una tasa de acierto del 100%. Generalmente, se considera que una tasa de acierto del 90% o superior es buena para una cache de Nivel 1 (L1). En la cache de Nivel 2 (L2), se considera aceptable una tasa de acierto superior al 50%. De ahí hacia abajo, el rendimiento de la aplicación puede caer de forma vertiginosa.

Se puede caracterizar el rendimiento promedio de lectura de la jerarquía de memoria al examinar la probabilidad de que una carga particular se satisfaga en un nivel particular de la jerarquía. Por ejemplo, asumamos una arquitectura de memoria con una velocidad de cache L1 de 10 ns, una velocidad en L2 de 30 ns y una velocidad de memoria de 300 ns. Si una referencia a memoria dada se satisface mediante la cache L1 el 75% de las veces, 20% de ellas en la L2, y 5% del tiempo en la memoria principal, el rendimiento promedio de la memoria será:

(0.75 * 10 ) + ( 0.20 * 30 ) + ( 0.05 * 300 ) = 28.5 ns
    

Puede usted notar fácilmente por qué es tan importante tener una tasa de éxito de 90% o más en la cache L1.

Dado que una memoria cache almacena sólo un subconjunto de la memoria principal en un momento dado, es importante mantener un índice de cuáles áreas de la memoria principal están almacenadas actualmente en la cache. Para reducir la cantidad de espacio que debe dedicarse a seguir la pista de las áreas de memoria en cache, ésta se divide en un número de ranuras de igual tamaño, conocidas como líneas. Cada línea contiene cierto número de localidades secuenciales de memoria, generalmente de cuatro a dieciseis números enteros o reales. Mientras que los datos adentro de una línea vienen todos de la misma porción de memoria, otras líneas pueden contener datos de partes alejadas de su programa, o tal vez datos provenientes de los programas de alguien más, como en Figure 1. Cuando usted solicita algo de la memoria, la computadora comprueba si tales datos están disponibles en alguna de esas líneas de cache. Si es el caso, los datos se regresan con un retraso mínimo. Si no, puede que su programa se retrase un poco, mientras se carga una nueva línea de la memoria principal. Por supuesto, si se trajo nuevo contenido para una línea, el contenido de ésta debió primero desalojarse. Si tiene suerte, no será aquella que contenga los datos que necesitará justamente después.

Figure 1
Las líneas de cache pueden venir de diferentes partes de la memoria.
Esta figura muestra un enrejado etiquetado, la Memoria Principal, y partiendo de un par de celdas en el enrejado hay flechas apuntando a la izquierda, a las líneas de cache en una caja. Las líneas se despliegan en una lista, etiquetadas de la 0 a la 3, etcétera.

En los multiprocesadores (computadoras con varias CPUs), los datos escritos deben regresarse a la memoria principal, de forma que el resto de los procesadores puedan verlos, o se debe tener a todos los demás procesadores al tanto de la actividad de la cache local. Tal vez se requiera decirles que invaliden las líneas antiguas que contienen valores previos de la variable escrita, para evitar que accidentalmente usen datos viejos. a esto se le denomina mantener la coherencia entre caches diferentes. El problema se puede volver muy complejo en un sistema multiprocesador.1

Las caches son efectivas porque los programas a menudo exhiben características que ayudan a mantener alta la tasa de aciertos. Estas características se llaman localidad de referenciaespacial y temporal; los programas frecuentemente hacen uso de datos e instrucciones que están cerca de otros datos e instrucciones, tanto en el espacio como en el tiempo. Cuando se carga una línea de cache de la memoria principal, no sólo contiene la información que causó el fallo de la cache, sino también algo de su información circundante. Hay buenas posibilidades de que la próxima vez que su programa necesite datos, éstos se encuentren en la línea de cache recién cargada o en alguna otra reciente.

Las caches trabajan mejor cuando un programa lee secuencialmente a través de la memoria. Asumamos que un programa está leyendo enteros de 32 bits con un tamaño de línea de cache de 256 bits. Cuando el programa hace referencia a la primera palabra en la línea de cache, debe esperar mientras dicha línea se carga desde la memoria principal. Las siete referencias a la memoria subsecuentes se satisfacerán rápidamente desde la cache. Esto se llama paso unitario porque la dirección de cada elemento de datos sucesivo se incrementa en uno, y se usan todos la datos cargados en la cache. El siguiente ciclo funciona con un paso unitario:


DO I=1,1000000 SUM = SUM + A(I) END DO

Cuando un programa accede a una estructura de datos grande usando "pasos no unitarios", el rendimiento sufre porque se cargan datos en cache que no se usan. Por ejemplo:


DO I=1,1000000, 8 SUM = SUM + A(I) END DO

Este código carga la misma cantidad de datos y experimenta el mismo número de fallas en cache que el ciclo previo. Sin embargo, el programa necesita sólo una de las ocho palabras de 32 bits cargadas en la cache. Incluso aunque este programa realiza 1/8 de las sumas que el ciclo anterior, el tiempo que dilata en ejecutarse es aproximadamente el mismo que el otro, porque las operaciones de memoria dominan el rendimiento.

Aunque este ejemplo puede parecer un poco artificial, hay muchas situaciones en las cuales ocurren frecuentemente pasos no unitarios. Primero, cuando se carga en FORTRAN un arreglo bidimensional en memoria, los elementos sucesivos en la primera columna se almacenan secuencialmente, seguidos por los elementos de la segunda columna. Si el arreglo se procesa colocando la iteración de los renglones en el ciclo más interno, produce un patrón de referencias de pasos unitarios como el siguiente:


REAL*4 A(200,200) DO J = 1,200 DO I = 1,200 SUM = SUM + A(I,J) END DO END DO

Resulta interesante señalar que muy probablemente un programador en FORTRAN escribirá el ciclo (en orden alfabético) como sigue, produciendo un incremento no unitario de 800 bytes entre operaciones de carga sucesiva:


REAL*4 A(200,200) DO I = 1,200 DO J = 1,200 SUM = SUM + A(I,J) END DO END DO

Por esta razón, algunos compiladores pueden detectar este orden de ciclos subóptimo e invertirán el orden de los ciclos para lograr un mejor uso del sistema de memoria. Sin embargo, como veremos en (Reference), esta transformación de código puede producir resultados diferentes, y así usted deberá darle "permiso" al compilador para intercambiar esos ciclos en este ejemplo particular (o, tras haber leído este libro, simplemente haberlo codificado apropiadamente desde el comienzo).

while ( ptr != NULL ) ptr = ptr->next;
    

El siguiente elemento que se recuerda se basa en el contenido del elemento actual. Este tipo de ciclo salta por toda la memoria sin un patrón particular. Se le conoce como caza de apuntadores, y no existe una forma acertada de mejorar el rendimiento de este código.

Un tercer patrón que se encuentra a menudo en cierto tipo de códigos se conoce como acopio (o dispersión), y ocurre en ciclos como:

SUM = SUM + ARR ( IND(I) )

donde el arreglo IND contiene desplazamientos dentro del arreglo ARR. De nuevo, tal como sucedió en la lista ligada, el patrón exacto de referencias a memoria sólo se conoce a tiempo de ejecución, cuando también se conocen los valores almacenados en el arreglo IND. Algunos sistemas de propósito especial tienen soporte de hardware especializado para acelerar esta operación en particular.

Footnotes

  1. (Reference) describe la coherencia entre caches con mayor detalle.

Content actions

Download module as:

Add module to:

My Favorites (?)

'My Favorites' is a special kind of lens which you can use to bookmark modules and collections. 'My Favorites' can only be seen by you, and collections saved in 'My Favorites' can remember the last module you were on. You need an account to use 'My Favorites'.

| A lens I own (?)

Definition of a lens

Lenses

A lens is a custom view of the content in the repository. You can think of it as a fancy kind of list that will let you see content through the eyes of organizations and people you trust.

What is in a lens?

Lens makers point to materials (modules and collections), creating a guide that includes their own comments and descriptive tags about the content.

Who can create a lens?

Any individual member, a community, or a respected organization.

What are tags? tag icon

Tags are descriptors added by lens makers to help label content, attaching a vocabulary that is meaningful in the context of the lens.

| External bookmarks