Você está na página 1de 8

Arquitectura e Ingeniera de Computadores

Examen nal de junio


Jueves, 19 de junio de 2008
Acuerdate de poner tu nombre en todas las hojas que utilices!
Justica claramente todas tus contestaciones!
Utiliza grupos de folios separados para responder a las cuestiones de cada parte
SOLUCIONES
PARTE ARQUITECTURAS MONOPROCESADOR
1. (2 puntos) Responder verdadero o falso a las siguientes cuestiones justicando adecuadamente
las respuestas.
a) (0,4 puntos) La planicacion estatica de instrucciones ayudada por una optimizacion basada
en la b usqueda de paralelismo por parte del compilador, es una tecnica com un empleada en los
procesadores superescalares actuales.
b) (0,4 puntos) En una maquina superescalar capaz de lanzar dos instrucciones por ciclo (1 de
punto jo y 1 de punto otante), con planicacion dinamica mediante Tomasulo y especulacion,
la presencia de riesgos WAW puede impedir el lanzamiento de dos instrucciones en un ciclo.
c) (0,4 puntos) Tanto los procesadores superescalares como los procesadores VLIW dependen mu-
cho de una adecuada prediccion de los saltos de la aplicacion.
d) (0,4 puntos) En las arquitecturas VLIW el procesador recibe el codigo libre de dependencias y
optimizado para la ejecucion paralela.
e) (0,4 puntos) En una arquitectura de tipo VLIW, las caractersticas de la arquitectura hacen
que la complejidad del banco de registros sea muy baja.
Solucion
a) Falso. Los procesadores superescalares actuales se basan en la planicacion dinamica de codigo,
la especulacion y tener implementado en hardware un algoritmo tipo Tomasulo para conseguir
una gran eciencia.
b) Falso. Una maquina superescalar con especulacion no presenta detenciones por riesgos de datos
en el lanzamiento de instrucciones. Concretamente, los riesgos WAW son resueltos por medio
del renombramiento implcito de registros que se realiza gracias a Tomasulo.
c) Verdadero. La prediccion de los saltos es la base para un buen funcionamiento de los procesa-
dores actuales, ya sean VLIW o superescalares. Lo que cambia en ellos es la manera de realizar
dicha prediccion: de forma dinamica, en los superescalares, de forma estatica en los procesadores
VLIW.
1
d) Verdadero. Los procesadores VLIW no incorporan chequeo de dependencias, por lo que es
mision del compilador preparar el codigo objeto para que este libre de dependencias y optimizado
para la ejecucion en una determinada arquitectura VLIW.
e) Falso. Todo lo contrario. Por las caractersticas del dise no VLIW, donde se trata de que en el
mismo ciclo de reloj se acceda al banco de registros por varias instrucciones, el dise no del banco
de registros es mas complejo, siendo este habitualmente un banco de registros partido en varios
sub-bancos.

2. (2 puntos) Tenemos un procesador superescalar homogeneo de grado 2 (en todas las etapas
se pueden manejar hasta 2 instrucciones por ciclo), con b usqueda alineada y emision alineada y en
orden. Para mantener la consistencia secuencial, as como para renombrar los registros y gestionar
las interrupciones se utiliza un ROB. Existe una estacion de reserva para cada unidad de ejecucion
y el predictor de saltos es estatico y predice que no va a saltar en los saltos hacia adelante, y
que si va a saltar en los saltos hacia atras. Se dispone de las siguientes unidades de ejecucion: dos
unidades de carga/almacenamiento (segmentadas) de latencia 2 (calcula la direccion en el primer
ciclo y accede a la cache en el segundo ciclo), dos ALUs enteras de latencia 1, un sumador en
coma otante (segmentado) de latencia 3, un divisor en coma otante (segmentado) de latencia 4,
y una unidad de saltos que resuelve los saltos en la etapa de ejecucion. Suponemos que en el cauce
de enteros estan implementados los cortocircuitos y reenvos habituales, pero no as en el cauce
de coma otante. Supon para este problema un tama no ilimitado del ROB y de cada una de las
estaciones de reserva.
En dicho ordenador se va a ejecutar la siguiente secuencia de instrucciones que calcula la division
de dos vectores x e y componente a componente:
; el valor inicial de n es bastante grande
lw r3, n ; r3 = n
slli r4, r3, #2 ; r4 = n * 4 (final del vector)
inicio: lf f0, x(r2) ; f0 = x(i)
lf f1, y(r2) ; f1 = y(i)
divf f2, f0, f1 ; f2 = x(i) / y(i)
sf f2, z(r2) ; z(i) = x(i) / y(i)
addui r2, r2, #4 ; r2 = r2 + 4 (incremento el desplazamiento)
sub r5, r4, r2 ; compruebo si he llegado al final
bnez r5, inicio ; saltar a inicio si r5 es distinto de 0
trap #0 ; fin del programa
<sgte.> ; suponemos las instrucciones siguientes enteras
Nota: La instruccion trap no causa la terminacion del programa hasta que se retira del ROB.
Considera que la primera vez que se accede a memoria (para obtener el valor de n y para obtener
el valor de los vectores a partir de r2) se produce un fallo de cache L1 que se resuelve en la cache
L2 con una latencia total de 3 ciclos en el acceso a memoria. El resto de accesos a memoria son
servidos por la cache L1 gracias a la tecnica del prefetching.
a) (1 punto) En que ciclo se conrma la primera instruccion de salto? En que ciclo se conrma la
segunda instruccion de salto? Dibuja el diagrama correspondiente para justicar tu respuesta.
En dicho diagrama indica, para cada instruccion y ciclo de reloj, que fase de la instruccion se
esta ejecutando.
b) (0,5 puntos) Cuantas instrucciones de las dos primeras iteraciones tendra el ROB al acabar
el ciclo en que se conrma la instruccion (addui r2, r2, #4) de la primera iteracion? Para
justicar la respuesta, muestra el contenido del ROB en dicho ciclo.
c) (0,5 puntos) Considera ahora que el procesador tiene un reloj de 1 GHz. y que el regimen
estacionario de ejecucion es igual al de la segunda iteracion. En cuanto tiempo (en segundos)
2
se ejecutara el codigo anterior para un tama no del vector de 1000 elementos? No hace falta que
tengas en cuenta el tiempo necesario para el llenado/vaciado del pipeline. Cuantos MFLOPs
obtendramos para el procesador descrito ejecutando el codigo anterior?
Solucion
a) Para obtener las dos preguntas de este apartado tenemos que simular la ejecucion del programa
durante las dos primeras iteraciones. Obtenemos el siguiente diagrama instruccionestiempo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
lw r3, n IF IS EX L2 L2 L2 WB C
slli r4, r3, #2 IF IS - - - - EX WB C
lf f0, x(r2) IF IS EX L2 L2 L2 WB C
lf f1, y(r2) IF IS EX - - L2 L2 L2 WB C
divf f2, f0, f1 IF IS - - - - - - EX EX EX EX WB C
sf f2, z(r2) IF IS EX - - - - - - - - - - C(S1)
addui r2, r2, #4 IF IS EX WB - - - - - - - - - C
sub r5, r4, r2 IF IS - EX WB - - - - - - - - C
bnez r5, inicio IF IS - EX - - - - - - - - - C
trap #0 IF XX
lf f0, x(r2) IF IS EX L1 WB - - - - - - - C
lf f1, y(r2) IF IS EX - L1 WB - - - - - - - C
divf f2, f0, f1 IF IS - - - EX EX EX EX WB - - C
sf f2, z(r2) IF IS EX - - - - - - - - - - C(S1)
addui r2, r2, #4 IF IS EX WB - - - - - - - - C
sub r5, r4, r2 IF IS - EX WB - - - - - - - - C
bnez r5, inicio IF IS - EX - - - - - - - - C
trap #0 IF XX
lf f0, x(r2) IF
lf f1, y(r2) IF
En el diagrama denotamos con L1 un acceso a memoria resuelto en la cache L1 y con L2 un
acceso a memoria satisfecho por la cache L2. Como podemos apreciar, los tres primeros accesos
a cache fallan en la L1, siendo resueltos en tres ciclos por la cache L2.
A partir del diagrama anterior vemos que la primera instruccion de salto se conrma en el ciclo
18, mientras que la segunda instruccion de salto lo hace en el ciclo 21.
b) La instruccion addui r2, r2, #4 de la primera iteracion ejecutada se conrma en el ciclo 17.
Al nal de dicho ciclo el contenido del ROB (solo instrucciones de las 2 primeras iteraciones) es
el siguiente:
Entrada ocupado instr. estado dest. valor
1 Si bnez r5, inicio EX
2 Si lf f0, x(r2) WB f0 Mem[x(r2)]
3 Si lf f1, y(r2) WB f1 Mem[y(r2)]
4 Si divf f2, f0, f1 WB f2 #2 / #3
5 Si sf f2, z(r2) EX Mem[z(r2)]
6 Si addui r2, r2, #4 WB r2 r2+4
7 Si sub r5, r4, r2 WB r5 r4 - #6
8 Si bnez r5, inicio EX
El n umero total de entradas del ROB ocupadas es de 8 al nal del ciclo 17. Como se puede ver,
hemos rellenado el campo valor para todas aquellas entradas que ya han pasado por su etapa
WB.
3
c) Del diagrama del apartado a) podemos ver que cada iteracion del bucle en el regimen estacio-
nario tarda en ejecutarse 4 ciclos de reloj (medido como la diferencia entre el comienzo de las
instrucciones de carga en el regimen normal). Por lo tanto, el tiempo que tardan en ejecutarse
1000 iteraciones, sin tener en cuenta los ciclos del llenado/vaciado del pipeline, es el siguiente:
Tiempo = 4 ciclos 1000 = 4000 ciclos = 4 seg
Para el calculo de los MFLOPs, tenemos que en cada iteracion solo realizamos una operacion
en coma otante (la division), por lo que obtendramos:
MFLOPs = 1 op / 4 ciclos @ 1 Ghz = 250 MFLOPs.

PARTE ARQUITECTURAS MULTIPROCESADOR


3. (3 puntos) Responder brevemente a cada una de las cuestiones teorico/practicas que a
continuacion se plantean (se valorara la capacidad de concrecion del alumno).
a) (0,25 puntos) Dada una aplicacion cientca que requiere la realizacion de 2 10
15
operaciones
de punto otante, y dado que disponemos de procesadores con 25 GFLOPS, cual es el n umero
mnimo teorico de procesadores que necesitaramos en nuestra maquina paralela para poder
ejecutar la aplicacion en 1 hora?Crees que dicho n umero de procesadores coincidira con el que
se necesitaran en la practica?
b) (0,25 puntos) Por que podemos argumentar que la Ley de Amdahl da una vision un tanto
pesimista de las ventajas del paralelismo?
c) (0,25 puntos) Cual de las dos organizaciones de directorio plano (basado en memoria o basado
en cache) es mas escalable desde el punto de vista del rendimiento?
d) (0,25 puntos) En que consiste la comparticion falsa (false sharing)? Desde el punto de vista
del programador, como crees que se podra evitar este fenomeno?
e) (0,25 puntos) Explica en que caso sera preferible realizar la transicion de estado M I frente
a la M S ante la ocurrencia de un fallo de lectura en un procesador remoto. Pon un ejemplo
de codigo (preferiblemente que aparezca en la practica 3) en el que se de dicha situacion.
f) (0,50 puntos) Supon un CMP con 4 n ucleos de ejecucion. Cada n ucleo tiene caches separadas
de primer nivel para datos (16 KB) e instrucciones (8 KB), mientras que la cache de segundo
nivel de 2 MB es compartida entre los 4 n ucleos. La cache L2 contiene todos los datos que se
almacenan en las caches L1 (se mantiene la inclusividad). La red de interconexion dentro del
CMP es un bus com un y para el mantenimiento de la coherencia de las caches L1 se emplea
un protocolo basado en sgoneo con los estados MOSI. Para la siguiente secuencia de eventos
sobre el mismo bloque de datos, indica en cada caso las transacciones de bus que se generan, el
vector de estados para el bloque de datos y quien proporciona el bloque de datos (memoria, L2
o L1 remota). Supon que inicialmente las caches estan vacas.
Lect(core 1) Escr(core 1) Lect(core 2) Lect(core 3) Escr(core 4) Lect(core 1)
reemplazo(core 4) Lect(core 2)
g) (0,25 puntos) Que diferencias hay entre el modelo de consistencia secuencial y el modelo de
consistencia debil estudiados en clase? Dado el siguiente fragmento de codigo, que resultados
son posibles bajo cada uno de los dos modelos? Suponer que inicialmente el valor de A es 0.
P1 P2
A = 1; BARRIER(b); {
BARRIER(b); print A;
4
h) (0,25 puntos) Explicar la principal diferencia entre la version de los cerrojos basada en las
instrucciones LL-SC y la basada en tickets estudiadas en clase.
i) (0,25 puntos) Explicar la implementacion software centralizada de las barreras con cambio de
sentido estudiada en clase. Con respecto a la implementacion software centralizada original,
que problema trata de resolver?
j) (0,25 puntos) Que diferencia hay entre las tecnicas de conmutacion virtual cut-through y worm-
hole?
k) (0,25 puntos) Pon un ejemplo de algoritmo de encaminamiento determinista para mallas n-
dimensionales y explica su funcionamiento.
Solucion
a) El n umero mnimo de procesadores que necesitamos (N) lo calculamos a partir de la siguiente
expresion:
210
15
N2510
9
= 3600, con lo que N = 23 procesadores. En la practica el n umero de
procesadores necesarios probablemente sera mayor, dado que estamos despreciando la sobrecarga
introducida por la parelizacion y suponemos que la aplicacion escala perfectamente con el n umero
de procesadores y todo el tiempo de ejecucion de la aplicacion esta dedicado a calculos en punto
otante que se reparten de manera perfecta entre todos los procesadores.
b) La ley de Amdahl nos dice que tenemos limitada la escalabilidad, y que este lmite depende de
la fraccion de codigo no paralelizable. Mas concretamente, en el razonamiento de Amdahl se
supone constante el tiempo de ejecucion de una aplicacion en un sistema uniprocesador y con
ello se considera tambien constante la fraccion de codigo paralelizable. Sin embargo, en muchas
ocasiones se puede incrementar la fraccion de codigo paralelizable aumentando el tama no del
problema que resuelve la aplicacion.
c) Desde el punto de vista del rendimiento ambas implementaciones dieren en el rendimiento
que obtienen las escrituras, mas concretamente en la latencia de las mismas. En el primer
caso, la latencia de las escrituras permanece casi constante conforme aumentamos el n umero de
procesadores (y por tanto, compartidores potenciales). Por el contrario, en el segundo esquema
la latencia de las escrituras se incrementa con el n umero de compartidores. De esta forma,
diremos que el primer esquema es mas escalable.
d) La comparticion falsa surge, por ejemplo, cuando dos procesos estan accediendo a partes distintas
de un mismo bloque de datos y al menos uno de ellos esta escribiendo. El programador podra
eliminar este fenomeno evitando que variables que no se comparten entre dos procesadores
caigan en la misma lnea de memoria. Para ello se podran declarar variables vacas (padding)
entre las declaraciones de las dos variables no compartidas.
e) La transicion M I sera preferible para bloques de datos que contienen variables que estan
siendo accedidas seg un un patron migrario por varios procesadores. Como ejemplo, la variable
global->di en la practica 3 se accede seg un ese patron migratorio (primero se lee y despues se
modica su contenido).
f) Inicialmente, el vector de estados para el bloque de datos sera V(c1, c2, c3, c4) = (I, I, I, I). Las
transacciones de bus que se generaran son:
Lect(core 1) : BusRd. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (S, I, I, I). El bloque de datos es proporcionado
por la memoria principal y cargado tambien el la L2 compartida.
5
Escr(core 1) : BusRdX. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (M, I, I, I). El bloque de datos es proporcionado
por la L2.
Lect(core 2) : BusRd. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (O, S, I, I). El bloque de datos es proporcionado
por la L1 del core 1.
Lect(core 3) : BusRd. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (O, S, S, I). El bloque de datos es proporcionado
por la L1 del core 1.
Escr(core 4) : BusRdX. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (I, I, I, M). El bloque de datos es proporcionado
por la L1 del core 1.
Lect(core 1) : BusRd. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (S, I, I, O). El bloque de datos es proporcionado
por la L1 del core 4.
reemplazo(core 4) : BusWB. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (S, I, I, I). El bloque de datos es actualizado en
la L2 compartida.
Lect(core 2) : BusRd. Una vez completada la transaccion de bus, el vector de estados
quedara de la forma V(c1, c2, c3, c4) = (S, S, I, I). El bloque de datos es proporcionado
por la L2 compartida.
g) El modelo de consistencia debil permite cualquier reordenacion entre escrituras y lecturas a
posiciones de memoria distintas por parte del mismo procesador (relaja WR, WW, RR,
R W), manteniendo el orden entre accesos solo en los puntos de sincronizacion del codigo. Para
el ejemplo que se nos da, el resultado de la ejecucion bajo ambos modelos sera la impresion de
un valor 1 por pantalla.
h) La principal diferencia es que la version basada en tickets es justa, es decir, otorga el cerrojo
en el mismo orden en el que los procesos van tratando de adquirirlo, mientras que la version
basada en LL-SC no.
i) El problema de la version original es que se puede producir un interbloqueo si se hace uso de
la misma variable barrera en dos puntos del programa. En concreto cuando se libera la barrera
en el primer punto, todos los procesos deben salir antes de que un proceso vuelva a entrar en
la misma variable barrera en el segundo punto. Como ejemplo, podramos tener que uno de los
procesos que alcanzo la barrera en el primer punto permanece expulsado de la CPU cuando el
ultimo proceso en llegar a la barrera la libera. Si uno de los procesos alcanza el segundo punto
del programa antes de que el proceso anterior haya visto la liberacion de la barrera se produce
el interbloqueo. Para evitar este problema, la version con cambio de sentido hace que la espera
ocupada se haga sobre valores distintos en llamadas consecutivas a la misma variable barrera,
de forma que no hay que inicializar la variable que empleamos para hacer la espera ocupada.
j) Ambas tecnicas de conmutacion segmentan el envo de los mensajes, la diferencia es que la
primera realiza el control de ujo sobre paquetes completos, lo que hace que los buers del
router tengan capacidad para almacenar al menos todo un paquete, mientras que la segunda
ejerce el control de ujo a nivel de it (unidades mas peque nas) de forma que el tama no de los
buers puede ser menor.
k) El ejemplo mas claro es el encaminamiento en orden de dimension (dimension order). En este
caso se recorren las dimensiones en un orden jo, agotando los desplazamientos en cada una de
ellas antes de pasar a la siguiente.

6
4. (3 puntos) Dado el siguiente programa secuencial escrito en C:
#include ...
if (max == 0) break;
oat key[256]; key index[pos] = 0;
unsigned char key index[8000]; /* valores entre 0 y 255 */ value = key[max];
oat A[8000][8000]; for (i=0;i<8000;i++) {
for (j=0;j<8000;j++) {
void initialize(void) { A[i][j] = A[i][j] * value;
/* Inicializa los arrays key y }
key index, y la matriz A */ }
} }
void solve(void) {
int max;
int i,j,pos;
oat value; int main() {
initialize();
while (1) { solve();
max = 0; return 0;
for (i=0;i<8000;i++) { }
if (key index[i]>max) {
max = key index[i];
pos = i;
}
}
muestra una version paralela del codigo
1
que utilice 4 hilos y que por tanto pueda ser ejecutado
en un CMP con 4 n ucleos. Supon que nos interesa paralelizar los accesos tanto a la matriz A como a
key index. Para ello, discute las fases en las que habra que organizar el codigo paralelo, las variables
que habra que declarar como compartidas, etc.
Solucion
El programa paralelo lo organizaramos en tres fases: (1) fase de inicializacion, en la que entre
otras cosas se crearan los 3 hilos adicionales para el calculo, (2) fase paralela, que consistira en
la ejecucion del procedimiento solve por parte de los cuatro hilos, y (3) fase de nalizacion en la
empleamos la llamada wait for end(). A su vez, el procedimiento solve se organizara en dos fases:
(1) calculo del maximo elemento en la porcion del array key index que cada hilo tiene asignada y
posteriormente calculo del maximo global de entre los maximos encontrados por cada hilo y (2)
actualizacion de la matriz A.
Los arrays key index y key, y la matriz A habra que declararlos como memoria compartida entre
todos los hilos. Ademas necesitamos un par de variables globales para almacenar el maximo global
(global max) y su posicion en el array key index (global pos). Finalmente necesitamos tambien una
variable global pid que vamos a utilizar para obtener el identicador unico que cada hilo va a tener
asignado. En cuanto a las primitivas de sincronizacion vamos a hacer uso de un cerrojo (l1) y una
barrera (b1).
El codigo pedido quedara como sigue:

1
Utiliza las macros parmacs vistas en clase:
lockdec(lock), lock(lock) y unlock(lock)
bardec(bar) y barrier(bar,num threads)
g malloc(num bytes), create(num threads, proc, arguments) y wait for end(num threads)
7
#include ...
if (max != 0) {
oat *key; LOCK(l1);
unsigned char *key index; if (max > var->global max) {
oat **A; var->global max = max;
struct { var->global pos = pos;
int global pid; }
int global max; UNLOCK(l1);
int global pos; }
} *var; BARRIER(b1);
LOCKDEC(l1); if (var->global max == 0) break;
BARDEC(b1); if (pid == 0) key index[var->global pos] = 0;
value = key[var->global max];
void initialize(void) { for (i=0;i<2000;i++) {
/* Inicializa los arrays key y for (j=0;i<8000;j++) {
key index, y la matriz A */ A[min i+i][j] = A[min i+i][j] * value;
} }
}
void solve(void) { BARRIER(b1);
int max,pid; }
int i,j,pos; }
oat value; int main() {
int min i; key = G MALLOC(array 1-d de 256 elementos oat);
key index = G MALLOC(array 1-d de 8000 elementos char);
LOCK(l1); A = G MALLOC(array 2-d de 8000 por 8000 elementos oat);
pid = var->global pid; var = G MALLOC(struct con 3 elementos enteros);
var->global pid++;
UNLOCK(l1); initialize();
min i = pid*2000; CREATE(3, solve);
while (1) { solve();
max = 0; WAIT FOR END(3);
if (pid==0) var->global max = 0; return 0;
for (i=0;i<2000;i++) { }
if (key index[min i+i]>max) {
max = key index[min i+i];
pos = min i+i;
}
}
8

Você também pode gostar