Segundo parcial Junio 2007
Segundo parcial Junio 2007
Segundo parcial Junio 2007
- No tags were found...
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Arquitectura e Ingeniería de Computadores<br />
2ō Parcial (Arquitecturas Multiprocesador)<br />
Jueves, 21 de junio de <strong>2007</strong><br />
¡Acuérdate de poner tu nombre en todas las hojas que utilices!<br />
¡Justifica claramente todas tus contestaciones!<br />
SOLUCIONES<br />
1. (3 puntos) Explicar brevemente cada una de las cuestiones que a continuación se plantean<br />
(se valorará la capacidad de concreción del alumno).<br />
a) (0,25 puntos) Dada un multiprocesador formado por varios nodos, cada uno con la estructura<br />
siguiente, explicar a qué categoría o categorías de las estudiadas en clase puede pertenecer la<br />
máquina.<br />
b) (0,25 puntos) ¿Qué significa la expresión speed-up superlineal? ¿Se te ocurre algún caso en que<br />
esto pudiera darse?<br />
c) (0,25 puntos) ¿En qué consiste un directorio jerárquico? ¿En qué se diferencia de un directorio<br />
plano basado en memoria?<br />
d) (0,25 puntos) Explicar la utilidad del estado E para un protocolo de coherencia de caches y el<br />
motivo por el que la inclusión éste es recomendable en arquitecturas SMP comerciales.<br />
e) (0,50 puntos) Para el siguiente fragmento de código explicar si es preferible (se producen menos<br />
fallos de cache) un protocolo de coherencia basado en actualización (DRAGON) o en invalidación<br />
(MESI). Suponer que inicialmente todas las variables están a cero, que flag y A están en líneas<br />
de memoria distintas y que las caches de los procesadores sólo contienen las variables locales de<br />
cada proceso.<br />
P1<br />
P2<br />
int i; int i;<br />
for (i=0; i
g) (0,25 puntos) ¿Qué es el ancho de banda de la bisección de una red? ¿Por qué es importante<br />
esta medida?<br />
h) (0,25 puntos) Explicar el algoritmo de encaminamiento “edge cube” para hipercubos.<br />
i) (0,25 puntos) Explicar el problema de la implementación software centralizada de las barreras<br />
estudiada en clase (ilustrar la explicación con un ejemplo).<br />
j) (0,25 puntos) Explicar el problema más importante que la implementación de los cerrojos basada<br />
en array (array-based lock) tiene en arquitecturas ncc-NUMA y cómo podría ser resuelto.<br />
Solución<br />
a) En primer lugar se trata de un multiprocesador de memoria compartida, puesto que la comunicación<br />
está integrada a nivel del sistema de memoria. Además, es un multiprocesador NUMA<br />
puesto que cada nodo tiene una memoria local “cercana”. Así pues, las categorías a las que podría<br />
pertenecer son cc-NUMA y ncc-NUMA.<br />
b) Significa que la ganancia en velocidad que obtenemos conforme aumentamos el número de procesadores<br />
se incrementa en mayor medida que el número de procesadores. Este fenómeno puede<br />
surgir como consecuencia de que al aumentar el número de procesadores también estamos aumentando<br />
el número de otros recursos (cache, memoria principal, ...), de lo que se pueden beneficiar<br />
algunas aplicaciones.<br />
c) En un directorio jerárquico la información de directorio se organiza en una estructura de datos<br />
lógica de tipo árbol, en la que las hojas representan los nodos de procesamiento, cada uno con<br />
su memoria local, y los nodos intermedios contienen la información de directorio. La diferencia<br />
con un directorio plano basado en memoria es que para éste último la información de directorio<br />
para cada bloque de memoria reside en un lugar fijo y conocido (el nodo home), mientras que<br />
para un directorio jerárquico hay que encontrarla atravesando la jerarquía.<br />
d) El estado E es un estado exclusivo (hay una única copia del bloque de datos en las caches) pero<br />
no propietario (memoria principal está actualizada). Un procesador puede escribir un dato en<br />
estado E sin generar fallo de cache, y por lo tanto permite que un programa secuencial genere el<br />
mismo número de fallos de cache en una arquitectura monoprocesador y en una multiprocesador.<br />
El motivo por el que se emplea en SMPs comerciales es porque uno de los usos más importantes<br />
de éstos es la ejecución de lotes de trabajos secuenciales (máquinas de throughput)<br />
e) El protocolo basado en actualización se comporta mejor. Hay dos variables compartidas: flag<br />
y A. La variable A es escrita por P1 y leída por P2, por lo que un protocolo de actualización<br />
generaría una transacción de Update en cada iteración del bucle (fallo de escritura), mientras<br />
que uno de invalidación generaría 2 (Fallo de escritura + fallo de lectura). Para la variable flag<br />
ocurre algo similar, se escribe y se lee en cada iteración del bucle (por procesadores diferentes),<br />
con lo que un protocolo basado en actualización genera menos fallos.<br />
f) Un modelo relajado de memoria trata de conseguir mejores prestaciones que el modelo de consistencia<br />
secuencial. Concretamente, trata de mantener un modelo de programación simple (aunque<br />
no tanto como la consistencia secuencial) pero permite reordenaciones de determinados accesos<br />
a memoria con el objetivo de mejorar el rendimiento. Para que el programador pueda forzar<br />
explícitamente los órdenes que el modelo relaja (y por lo tanto, se comporte como el modelo de<br />
consistencia secuencial) se incluyen instrucciones especiales en el ISA del procesador.<br />
2
g) Se define como la suma de los anchos de banda del mínimo número de enlaces que separan la<br />
red en dos mitades. Da una idea del ancho de banda máximo de una red de interconexión, ya<br />
que bajo tráfico uniforme la mitad de los mensajes habrán de “cruzar” la bisección.<br />
h) Cada nodo en un hipercubo viene identificado por un número binario. Para hacer llegar un<br />
mensaje desde un nodo origen hasta uno destino se calcula la operación XOR de los identificadores<br />
binarios de origen y destino y se cruzan (en un determinado orden) aquellas dimensiones para<br />
las que el resultado de la operación XOR es 1.<br />
i) El problema es que se puede producir un interbloqueo si se hace uso de la misma variable barrera<br />
en dos puntos del programa. En concreto cuando se libera la barrera en el primer punto, todos<br />
los procesos deben salir antes de que un proceso vuelva a entrar en la misma variable barrera<br />
en el segundo punto. Como ejemplo, podríamos tener que uno de los procesos que alcanzó la<br />
barrera en el primer punto permanece expulsado de la CPU cuando el último proceso en llegar a<br />
la barrera la libera. Si uno de los procesos alcanza el segundo punto del programa antes de que<br />
el proceso anterior haya visto la liberación de la barrera se produce el interbloqueo.<br />
j) El problema es que la dirección de memoria sobre la que un proceso ha de realizar la espera<br />
activa no es conocida a priori en esta implementación de los locks. De esta forma, esta dirección<br />
podría corresponder con una memoria remota, con lo que no podría ser almacenada en la<br />
cache del proceso que realiza la espera activa. Esto ocasionaría una gran cantidad de tráfico<br />
(una transacción de red por cada lectura hecha durante la espera activa). La solución pasa por<br />
modificar la implementación de los cerrojos para asegurar que cada procesador realiza la espera<br />
activa sobre una posición de memoria local. Esto se hace, por ejemplo, en la implementación de<br />
los cerrojos con encolado software (Software Queuing Lock).<br />
2. (3 puntos) Suponer un multiprocesador SMP con 10 procesadores y el protocolo de coherencia<br />
de caches MSI estudiado en clase. Para implementar las barreras se ha optado por la versión software<br />
centralizada con “cambio de sentido” (sense-reversal) estudiada en clase.<br />
a) (0,5 puntos) Explica las variables compartidas necesarias para llevar a cabo esta implementación,<br />
así como el cometido de cada una de ellas. Para cada cada variable, indica también por cuáles<br />
de los procesos es accedida y cómo (por ejemplo, leída)<br />
b) (1 punto) Dado que el multiprocesador proporciona la versión de los cerrojos ticket-lock estudiada<br />
en clase, calcula el número de fallos de cache que se producirían cuando 10 procesos (cada uno<br />
ejecutándose en un procesador diferente) atravesaran una barrera. Para dicho cálculo suponer<br />
que los procesos alcanzan la llamada BARRIER exactamente a la misma vez y que el árbitro<br />
de bus otorga el control del mismo siguiendo un orden FIFO (para las peticiones que llegan al<br />
mismo tiempo el árbitro da prioridad a las de los procesadores con un identificador más bajo),<br />
que inicialmente la cache asociada a cada procesador no almacena ninguna variable compartida<br />
(sí las variables locales al proceso), y que las variables compartidas identificadas en el apartado<br />
a) residen en bloques de memoria diferentes.<br />
c) (0,5 puntos) Cambiamos el protocolo de coherencia MSI por uno MESI optimizado para el patrón<br />
migratorio (implementa la transición M → I para BusRead y el bloque es cargado en estado<br />
E en la cache del procesador que tiene el fallo de lectura 1 ). Bajo las mismas condiciones del<br />
apartado anterior, ¿cuántos fallos de cache se producirían ahora?<br />
d) (0,5 puntos) El ISA de los procesadores de la máquina dispone del par de instrucciones LL y SC,<br />
que podrían ser empleadas directamente en el código de las barreras para ahorrar las llamadas<br />
a los procedimientos de librería LOCK y UNLOCK. Muestra cómo quedaría el código de la<br />
implementación de las barreras en este caso.<br />
1 Notar que esto obliga a notificar a memoria los reemplazos de bloques de datos en estado E<br />
□<br />
3
e) (0,5 puntos) Para el nuevo código de las barreras, ¿cuántos fallos de cache se producirían?<br />
Solución<br />
a) La implementación software de las barreras centralizadas con “sense-reversal” emplea tres variables<br />
compartidas:<br />
counter: variable de tipo entero que se va incrementando con cada proceso que llega a la<br />
barrera. Cuando su valor alcanza el número de procesos se procede a la liberación de la<br />
barrera. Esta variable es leída y escrita por todos los procesos.<br />
lock: variable de tipo cerrojo que es empleada para asegurar el acceso en exclusión mutua<br />
a la variable anterior. Este cerrojo es adquirido y liberado por todos los procesos.<br />
flag: variable de tipo entero usada para realizar la espera ocupada. Todos los procesos la<br />
leen salvo el último en llegar a la barrera, que modifica su valor para notificar la liberación<br />
de la misma al resto.<br />
b) Para calcular el número de fallos de cache que se producen para que todos los procesos logren<br />
pasar la barrera, vamos a mostrar lo que ocurriría para el primer proceso que entra en la misma:<br />
El número de fallos de cache que se producen hasta que el primer proceso logra adquirir el<br />
cerrojo son: 10 como consecuencia de la instrucción fetch&increment (uno por proceso) +<br />
10 como consecuencia de la lectura de now_serving (uno por proceso).<br />
Después, dentro de la sección crítica el proceso leerá y posteriormente escribirá el contenido<br />
del contador compartido, lo que ocasionará 2 fallos de cache con el protocolo MSI de clase.<br />
Finalmente, el lock es liberado mediante una escritura de now_serving que ocasiona un<br />
fallo de cache y el proceso que efectúa la liberación queda realizando la espera activa sobre<br />
el flag compartido, lo que ocasiona un nuevo fallo de cache.<br />
En total se producen 24 fallos de cache. El resto de procesos irán entrando en la sección crítica<br />
conforme vayan viendo que el valor del contador now_serving coincide con el valor de su ticket.<br />
De esa forma el número de fallos como consecuencia de la lectura de now_serving se irá reduciendo<br />
en 1 conforme un nuevo proceso entre en la sección crítica y el número de fallos de cache<br />
para el resto de procesos salvo el último será de ∑ 7<br />
i=0<br />
(13 − i). El último proceso en entrar a la<br />
barrera procede de la misma forma, salvo que en lugar de realizar la espera activa sobre el flag,<br />
escribe esta variable para notificar la liberación de la barrera, así como resetea el contador. Esto<br />
ocasiona 1 fallo de cache para escribir la variable flag. Para resetar el contador, sin embargo, no<br />
se produce fallo de cache, puesto que el proceso ya lo modificó con anterioridad y está en estado<br />
M en su cache. Finalmente, los procesos que están en la espera ocupada sufren 1 fallo de lectura<br />
cada uno para “ver” el nuevo valor de flag.<br />
De esta forma, el número total de fallos de cache es de: 24 + 13 + 12 + 11 + 10 + 9 + 8 + 7 +<br />
6 + 5 + 9 = 114.<br />
c) La sustitución del protocolo MSI de clase por el MESI con la optimización del patrón migratorio<br />
descrita en el enunciado supone las siguientes modificaciones en cuanto a comportamiento se<br />
refiere:<br />
La primera lectura a un bloque de datos hará que se cargue en cache en estado E, y por lo<br />
tanto no ocasionará fallo en caso de que el procesador quiera escribirlo con posterioridad.<br />
4
Cuando un procesador sufre un fallo de lectura para un bloque que otro procesador tiene en<br />
estado M, el bloque de datos se trae en estado E (en lugar de S), con lo que una posterior<br />
escritura por parte del que tiene el fallo de lectura no genera fallo de cache.<br />
De esta forma, nos ahorramos uno de los dos fallos que se producirían en la sección crítica como<br />
consecuencia de la lectura y posterior modificación de la variable counter, dado que la lectura<br />
traería el bloque de datos en estado E, con lo que la escritura posterior no ocasionaría fallo.<br />
De esta forma, el número total de fallos de cache es de: 23 + 12 + 11 + 10 + 9 + 8 + 7 + 6 +<br />
5 + 4 + 9 = 104.<br />
d) El pseudocódigo de la implementación de las barreras con cambio de sentido utilizando el par<br />
LL/SC quedaría como sigue:<br />
struct bar_type {<br />
int counter;<br />
int flag = 0;}<br />
bar_name;<br />
BARRIER (bar_name, p) {<br />
local_sense = !(local_sense);<br />
incr: ll reg1, bar_name.counter<br />
addi reg1, reg1, 1<br />
sc bar_name.counter, reg1<br />
beqz incr<br />
}<br />
if (reg1 == p-) {<br />
bar_name.counter = 0;<br />
bar_name.flag = local_sense;<br />
} else<br />
while (bar_name.flag != local_sense) {};<br />
e) Asumiendo el protocolo MSI, el número de fallos de cache para esta nueva implementación se<br />
ve reducido de manera significativa. Cuando el primer proceso atraviesa la barrera se producen<br />
10 fallos para las instrucciones de LL, 1 fallo para el SC que tiene éxito. Después todos los<br />
procesos salvo éste vuelven a intentar el LL. El proceso que ha logrado aumentar el valor de<br />
counter observaría un nuevo fallo de cache cuando intentase leer la variable flag usada para la<br />
implementación de la espera activa. En total 12 fallos de cache.<br />
De esta forma, el número total de fallos sería: ∑ 9<br />
i=0 (12 − i) + 9 = 84 □<br />
3. (1,5 puntos) Para el siguiente segmento de código indica, justificando la respuesta, cuáles son<br />
los resultados posibles que se imprimirían por pantalla en los siguientes casos (supón que todas las<br />
variables están inicialmente a cero):<br />
P1 P2 P3<br />
A = 1 while (!flag1) {} while (!flag2) {}<br />
flag1 = 1 B = 2 if (!flag1) print (“Error”)<br />
flag2 = 1 else print (A,B)<br />
a) (0,5 puntos) Multiprocesador que implementa el modelo de consistencia secuencial.<br />
5
) (0,5 puntos) Multiprocesador que implementa un modelo de consistencia tipo PSO y preserva<br />
la atomicidad de las escrituras.<br />
c) (0,5 puntos) Multiprocesador que implementa el modelo de consistencia tipo PSO con escrituras<br />
no atómicas.<br />
Solución<br />
a) Bajo el modelo de consistencia secuencial el único resultado que podría dar la ejecución del<br />
programa es la impresión por pantalla del par (A,B) = (1,2). Puesto que la escritura de flag1 y<br />
flag2 se produce después de actualizar los valores de A y B, y además, la escritura de flag2 se<br />
produce después de la de flag1, éste es el único resultado posible.<br />
b) El modelo de consistencia PSO permite que una operación de lectura se complete antes que<br />
una escritura que la precede en el orden de programa, y que escritura a distintas posiciones de<br />
memoria puedan finalizar en desorden. Por otro lado el que las escrituras sean atómicas significa<br />
que son visibles a todos los procesos a la misma vez (una vez un proceso observa el valor de una<br />
escritura ésta es vista también por el resto).<br />
Para nuestro código, la atomicidad de las escrituras asegura que no se va a imprimir el mensaje<br />
de error por pantalla, ya que el hecho de que P2 vea la escritura a flag1 por parte de P1 significa<br />
que P3 también la va a ver tras observar el cambio del valor de flag2. Sin embargo, puesto que el<br />
orden de las escrituras que cada proceso hace no está garantizado de ninguna forma, el programa<br />
podría imprimir por pantalla uno de los siguiente 4 valores para el par (A,B) = {(0,0), (1,0),<br />
(0,2), (1,2)}.<br />
c) Finalmente, si eliminamos la restricción de que las escrituras sean atómicas, además de los<br />
valores para (A,B) mostrados en el apartado anterior, la ejecución del código podría resultar en<br />
la impresión del mensaje de error por pantalla, ya que P3 podría observar la modificación de<br />
flag2 por parte de P2 pero no la de flag1 por parte de P1.<br />
4. (2,5 puntos) Dado un multiprocesador de memoria compartida cc-NUMA con la siguiente<br />
configuración:<br />
El multiprocesador está formado por 16 nodos.<br />
Cada nodo consta de 1 procesador con una frecuencia de 2 GHz, una cache L2 de 1 MB (el<br />
procesador dispone además de caches L1 separadas para datos e instrucciones) y una porción<br />
de 64 GBytes de la memoria total de la máquina.<br />
El tiempo de acceso a memoria es de 150 ciclos de procesador, mientras que acceder a la L2<br />
requiere 15 ciclos de procesador (tanto para accesos locales como remotos).<br />
El tamaño para las líneas de cache L2 es de 64 bytes.<br />
La coherencia de las caches L2 se mantiene empleando el protocolo de coherencia basado en<br />
directorio estudiado en clase (protocolo MSI), extendido con los estados O y E (protocolo<br />
MOESI) y se usa full-map (vector de bits) como código de compartición. Se implementan<br />
las transacciones de red RedUpgr y RedUpgrOK. El directorio se almacena en memoria<br />
principal y el acceso al mismo se realiza en paralelo con el acceso al bloque de memoria<br />
correspondiente. Suponemos además que las escrituras a memoria por parte del controlador<br />
□<br />
6
de memoria (tanto al directorio como a bloques de datos) ocurren instantáneamente. Además,<br />
cuando un fallo de escritura requiere el envío de varios mensajes de invalidación éstos han<br />
de ser enviados en secuencia por parte del controlador de directorio (no envía una nueva<br />
invalidación hasta haber recibido la confirmación para la anterior).<br />
La red de interconexión es una malla 4×4 con encaminamiento X-Y, los enlaces tienen 8 bytes<br />
de ancho y una frecuencia de 500 MHz. Se emplea la estrategia de conmutación de conmutación<br />
de paquetes (store-and-forward), y suponemos que el router no introduce ningún retardo en<br />
los mensajes (solamente los enlaces). El tamaño de un mensaje que no lleva datos es de 8<br />
bytes (para los de datos habría que sumar el número de bytes de datos transmitidos).<br />
Los nodos se numera de izquierda a derecha de arriba a abajo empezando por el cero.<br />
a) (0,75 puntos) Suponer la siguiente secuencia de operaciones de memoria sobre el bloque B<br />
realizada por tres procesadores (las operaciones ocurren en el orden en que aparecen en la<br />
secuencia y una operación no comienza hasta que la que la precede ha finalizado, el nodo origen<br />
para este bloque es el 15). Mostrar el vector de estados en cache del bloque de datos después<br />
de cada operación así como indicar qué operaciones de memoria ocasionarán fallos de L2 y<br />
para éstas mostrar el estado del directorio antes y después de cada fallo de cache, así como<br />
las transacciones de red que generaría el directorio para resolverlo:<br />
r0 w0 r1 r15 w0 r0 r1 w1 r0 r15<br />
b) (0,25 puntos) Calcular la memoria total requerida por la estructura de directorio así como la<br />
sobrecarga de memoria que se introduce (considerar únicamente el código de compartición).<br />
c) (0,25 puntos) Como alternativa a la organización de directorio anterior se considera la utilización<br />
de punteros limitados como código de compartición. Concretamente se dispone de dos punteros<br />
por entrada de directorio y como estrategia de desbordamiento la de eliminar a uno de los compartidores<br />
(el que tenga el bloque en estado S y en caso de empate el más antiguo). Caso de<br />
desbordamiento lo primero que hace el controlador de directorio es liberar el puntero correspondiente.<br />
Calcular la sobrecarga de memoria en este caso (considerar de nuevo únicamente el código<br />
de compartición).<br />
d) (0,5 puntos) Repetir el apartado a) suponiendo la organización para el directorio del apartado<br />
anterior.<br />
e) (0,75 puntos) Vamos a calcular el impacto que sobre el rendimiento tiene el esquema de punteros<br />
limitados anterior. Para ello vamos a usar como métrica el tiempo de directorio medio por fallo<br />
de L2 (se define como el número total de ciclos de procesador que el directorio emplea en<br />
resolver todos los fallos de L2, dividido entre el número total de fallos resueltos). Calcula el<br />
tiempo de directorio medio por fallo de L2 para ambas organizaciones de directorio, e indica qué<br />
organización es mejor y por cuánto.<br />
Solución<br />
a) La siguiente tabla resume la información pedida. Los distintos accesos a memoria han sido<br />
numerados según la posición que ocupan en la secuencia. Los aciertos en cache han sido marcados<br />
como hit. En cursiva se muestran las transacciones de red que no son generadas por el directorio,<br />
sino por las caches en respuesta a una solicitud por parte de éste.<br />
b) Cada nodo tiene una porción de 64 GB de la memoria total y el tamaño de la línea de cache es<br />
de 64 bytes. De esta forma, el número total de líneas de memoria para cada nodo es de<br />
7
Estado<br />
Directorio Directorio en caches<br />
antes después (P 0 , P 1 , P 15 ) Transacciones de red<br />
(1) r0 NC {} P {P 0 } (E,I,I) RedDato<br />
(2) w0 Hit (M,I,I) –<br />
(3) r1 P {P 0 } O {P 0 , P 1 } (O,S,I) RedBusc → RedBuscOK<br />
(4) r15 O {P 0 , P 1 } O {P 0 , P 1 , P 15 } (O,S,S) RedBusc → RedBuscOK<br />
(5) w0 O {P 0, P 1, P 15} P {P 0} (M,I,I) RedInv 1 → RedInvOK 1 →<br />
RedInv 15 → RedInvOK 15 → RedUpgrOK<br />
(6) r0 Hit (M,I,I) –<br />
(7) r1 P {P 0} O {P 0, P 1} (O,S,I) RedBusc → RedBuscOK<br />
(8) w1 O {P 0 , P 1 , P 15 } P {P 1 } (I,M,I) RedInv 0 → RedInvOK 0 → RedUpgrOK<br />
(9) r0 P {P 1 } O {P 0 , P 1 } (S,O,I) RedBusc → RedBuscOK<br />
(10) r15 O {P 0 , P 1 } O {P 0 , P 1 , P 15 } (S,O,S) RedBusc → RedBuscOK<br />
64×2 30 Bytes<br />
64Bytes<br />
= 2 30 .<br />
Cada entrada de directorio contiene un vector de bits (16 bits, uno por nodo) con el que se<br />
identifica a todos los compartidores. De esta forma, la cantidad de memoria total por nodo para<br />
el directorio será de 2 30 × 2 = 2 31 Bytes. Multiplicando dicha cantidad por el número total de<br />
nodos obtenemos que la cantidad de memoria para el directorio en todo el multiprocesador es<br />
de 32 GB.<br />
La sobrecarga de memoria considerando únicamente el código de compartición la podemos obtener<br />
dividiendo el tamaño del mismo entre el tamaño de la línea de cache, o lo que es lo mismo<br />
2Bytes<br />
64Bytes<br />
= 0, 03125 → 3, 125 %.<br />
c) La sobrecarga de memoria en este caso (de nuevo considerando únicamente el código de compartición)<br />
la podemos obtener dividiendo el tamaño del nuevo código de compartición (2 punteros)<br />
entre el tamaño de la línea de cache. Puesto que la máquina cuenta con 16 procesadores, cada<br />
puntero tendrá log 2 16 = 4 bits, con lo que los dos punteros suman 1 Byte. De esta forma<br />
nos queda que la sobrecarga de memoria se reduce a la mitad con respecto a la que teníamos<br />
originalmente:<br />
1Byte<br />
64Bytes<br />
= 0, 015625 → 1, 5625 %.<br />
d) Los accesos a memoria que se verían afectados por el cambio en la estructura del directorio son<br />
los marcados con los números (4), (5) y (10). Para (4) y (10) el número de compartidores final<br />
excede el número de punteros disponibles, por lo que para poder proporcionar la copia del bloque<br />
de datos en cada uno de esos accesos hay que invalidar primero a uno de los dos compartidores<br />
ya existentes. Por su parte, el acceso (5) necesita enviar una invalidación menos.<br />
Estado<br />
Directorio Directorio en caches<br />
antes después (P 0, P 1, P 15) Transacciones de red<br />
(4) r15 O {P 0, P 1} O {P 0, P 15} (O,I,S) RedInv 1 → RedInvOK 1 →<br />
RedBusc → RedBuscOK<br />
(5) w0 O {P 0, P 15} P {P 0} (M,I,I) RedInv 15 → RedInvOK 15 → RedUpgrOK<br />
(10) r15 O {P 0, P 1} O {P 1, P 15} (I,O,S) RedInv 0 → RedInvOK 0 → RedBusc → RedBuscOK<br />
e) Calculamos ahora el número de ciclos de procesador que emplea el directorio en resolver cada<br />
fallo de L2 para el esquema de directorio original:<br />
(1) r0: en este caso el fallo lo resuelve una vez ha obtenido el bloque de datos de memoria<br />
principal (según nos dice el enunciado éste se obtiene a la misma vez que la información de<br />
directorio) → 150 ciclos.<br />
8
(3) r1: una vez se obtiene la información de directorio se pasa a buscar la copia del bloque<br />
del procesador 0 (es la única válida). Ello requiere enviar la transacción de red de RedBusc<br />
del nodo 15 al 0, que según el encaminamiento X-Y supondrá atravesar 6 enlaces de la red,<br />
después habrá que acceder a la cache remota para buscar el dato y finalmente la recepción<br />
por parte del directorio de la respuesta al mensaje de búsqueda (por la semántica del estado<br />
O este mensaje no llevará copia del bloque de datos) dará por finalizado el fallo. De esta<br />
manera, el número total de ciclos de procesador necesarios:<br />
150 + 6 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 6 × 1 × 4 = 150 + 24 + 15 + 24 = 213 ciclos de<br />
procesador.<br />
(4) r15: al igual que en caso anterior, una vez se obtiene la información de directorio se<br />
pasa a buscar la copia del bloque del procesador 0 (que es el que tiene el dato en estado O).<br />
De esta manera, el número total de ciclos de procesador coincide con el del caso anterior:<br />
150 + 6 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 6 × 1 × 4 = 150 + 24 + 15 + 24 = 213 ciclos de<br />
procesador.<br />
(5) w0: en este caso anterior, una vez se obtiene la información de directorio se pasa a<br />
invalidar las copias del bloque de los procesadores 1 y 15. Ello requiere enviar dos mensajes<br />
de invalidación desde el nodo 15, que además habrán de ser serializados según dice el enunciado<br />
(el segundo mensaje no se envía hasta que la confirmación de la primera invalidación<br />
ha sido recibida por el directorio). El envío de la invalidación al nodo 1 require atravesar 5<br />
enlaces, después habrá que acceder a la cache remota para invalidar y finalmente la recepción<br />
por parte del directorio de la respuesta a la invalidación hará que se envíe la segunda<br />
invalidación al nodo 15. En este caso no hay que salir a la red puesto que la invalidación<br />
va dirigida a la cache local. La respuesta a esta invalidación dará por finalizado el fallo. De<br />
esta manera, el número total de ciclos de procesador necesarios:<br />
150+5× 8Bytes<br />
8Byte<br />
×4+15+5×1×4+15 = 150+20+15+20+15 = 220 ciclos de procesador.<br />
(7) r1: al igual que en caso (3), una vez se obtiene la información de directorio se pasa a<br />
buscar la copia del bloque del procesador 0 (que es el que tiene el dato en estado O). De<br />
esta manera, el número total de ciclos de procesador coincide con el del caso anterior:<br />
150 + 6 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 6 × 1 × 4 = 150 + 24 + 15 + 24 = 213 ciclos de<br />
procesador.<br />
(8) w1: este caso es similar al (5), una vez se obtiene la información de directorio se pasa a<br />
invalidar la copia del bloque del procesador 0. El envío de la invalidación al nodo 0 require<br />
atravesar 6 enlaces. De esta manera, el número total de ciclos de procesador necesarios:<br />
150 + 6 × 8Bytes<br />
8Byte<br />
× 4 + 15 + 6 × 1 × 4 + 15 = 150 + 24 + 15 + 24 = 213 ciclos de procesador.<br />
(9) r0: de manera similar al caso (3), una vez se obtiene la información de directorio se<br />
pasa a buscar la copia del bloque del procesador 1. De esta manera, el número total de<br />
ciclos de procesador sería:<br />
150 + 5 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 5 × 1 × 4 = 150 + 20 + 15 + 20 = 205 ciclos de<br />
procesador.<br />
(10) r15: igual que en caso anterior, una vez se obtiene la información de directorio se pasa<br />
a buscar la copia del bloque del procesador 1. De esta manera, el número total de ciclos de<br />
procesador coincide con el del caso anterior:<br />
150 + 5 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 5 × 1 × 4 = 150 + 20 + 15 + 20 = 205 ciclos de<br />
procesador.<br />
Por lo tanto, el tiempo de directorio medio por fallo de L2 para el caso del directorio full-map<br />
es 150+213+213+220+213+213+205+205<br />
8<br />
= 204 ciclos de procesador.<br />
Para el caso del directorio con punteros limitados tan sólo cambia el tiempo de directorio para<br />
los accesos (4), (5) y (10):<br />
9
(4) r15: antes de poder enviar el RedBusc hay que invalidar la copia en el nodo 1 para<br />
liberar el puntero. Esto supone añadir los siguientes ciclos a los 213 que ya teníamos:<br />
213 + 5 × 8Bytes<br />
8Bytes<br />
× 4 + 15(cacheremota) + 5 × 1 × 4 = 213 + 20 + 15 + 20 = 268 ciclos de<br />
procesador.<br />
(5) w0: puesto que el acceso anterior supuso la invalidación de la copia del nodo 1, estos<br />
ciclos nos los ahorramos ahora:<br />
220 − (5 × 1 × 4 + 15 + 5 × 1 × 4) = 165 ciclos de procesador.<br />
(10) r15: al igual que para el acceso (4), antes de poder enviar el RedBusc hay que invalidar<br />
la copia en el nodo 0 para liberar el puntero. Esto supone añadir los siguientes ciclos a los<br />
213 que ya teníamos:<br />
205 + 6 × 1 × 4 + 15(cacheremota) + 6 × 1 × 4 = 205 + 63 = 268 ciclos de procesador.<br />
Por lo tanto, el tiempo de directorio medio por fallo de L2 para el caso del directorio con<br />
punteros limitados es 150+213+268+165+213+213+205+268<br />
8<br />
= 212 ciclos de procesador, con lo<br />
que el rendimiento se degrada un 3,9 %.<br />
□<br />
10