Sunteți pe pagina 1din 38

Capítulo 2

Segmentación de Cauce

En el presente capítulo se proponen y resuelven una serie de problemas relacionados con


la segmentación de cauce (pipelining) y los procesadores segmentados. En primer lugar
se presentan las expresiones que permiten estimar el rendimiento de un cauce, para pasar
después al estudio de algunos de los problemas que pueden reducir el rendimiento de los
procesadores segmentados, entre los que están los atascos por colisiones en el uso de
recursos o riesgos estructurales, los atascos por dependencias entre datos o riesgos de
datos, y los atascos debidos a los saltos o riesgos de control. Además de estos riesgos,
también se describirá cómo se procesan las interrupciones. El capítulo termina con
algunos problemas dedicados al diseño óptimo de unidades de control en cauces
segmentados.

La segmentación de cauce consiste en diseñar el circuito que implementa una función


determinada de manera que las distintas fases que deben completarse para realizar dicha
función se lleven a cabo a través de etapas sucesivas que funcionan independientemente,
de forma que cada etapa procesa las salidas que genera la etapa anterior y proporciona las
entradas a la siguiente etapa. Con la segmentación de cauce se consigue aumentar la
velocidad a la que se procesan las funciones (aumentar el número de funciones
terminadas por unidad de tiempo) en lugar de reducir el tiempo de procesamiento de la
función. La idea de la segmentación de cauce no es otra que la de las cadenas de montaje
típicas en la industria y cuyo origen se sitúa en la fabricación del modelo T de Ford.

Así, si partimos de un circuito no segmentado que procesa una función determinada en un


tiempo T, y se diseña un circuito segmentado cuyas etapas tardan un tiempo t (t < T) en
procesar cada una de las fases por las que hay que pasar para completar el procesamiento
de la función, se tiene que, el tiempo necesario para completar el procesamiento de n
funciones en el caso no segmentado es:

T orig  n  n  T (1)

y en el caso segmentado

Tseg  n  TLI   n  1  t (2)

donde TLI es el tiempo de latencia inicial, es decir el tiempo que tarda en procesarse la
primera de las n funciones. Como se puede ver, una vez que ha terminado de procesarse
la primera de las funciones a procesar, dado que todas las etapas del cauce estarían
ocupadas en una de las restantes funciones, esas funciones van terminando una a una
transcurrido únicamente un intervalo de tiempo t. El valor del TLI será igual al número de
etapas del cauce, k, multiplicado por el intervalo de tiempo, t (TLI = k×t). Es decir, la
ganancia de velocidad que se consigue con la segmentación de cauce en k etapas es igual
a:
T orig  n nT nT
S k  n    (3)
Tseg  n TLI   n  1  t  k   n  1   t

Como se puede ver, en el límite, cuando el número de funciones procesadas es lo


suficientemente elevado como para que TLI sea despreciable frente a (n – 1) × t (y por
supuesto, además 1 sería despreciable frente a n), la ganancia de velocidad que se obtiene
tiende a:

T
S max  lim S k  n  (4)
n t

Cuanto menor sea el tiempo de una etapa del cauce, t, frente al tiempo total de
procesamiento no segmentado de la función, T, mayor será la ganancia de velocidad que
se podría obtener cuando se procesa un número elevado de funciones de forma continua.
En el caso ideal en el que el tiempo no segmentado de la función sea igual a la suma del
tiempo de las etapas, es decir, T = k × t, entonces, sustituyendo en la expresión anterior,
Smax = k. Es decir, la máxima ganancia alcanzable sería igual al número de etapas del
cauce. Al realizar un diseño segmentado de un cauce, los tiempos de procesamiento de las
etapas pueden tener distintos de procesamiento, t1, t2,…., tk. La más lenta de esas etapas es
la que determinará el ritmo a la que las funciones van pasando de etapa a etapa, es el
cuello de botella que determina el tiempo de retardo que tendrán todas las etapas.
Además, para acoplar una etapa con la siguiente se introducen registros de acoplo donde
se almacena el resultado de una etapa y desde lo leerá la etapa siguiente. Si denominamos
d al retardo que introduce el registro de acoplo entre dos etapas, tendremos que el tiempo
de etapa en el cauce, t, será igual a

t  max t 1 , t 2 , , t k   d (5)

La productividad de un cauce de k etapas, Wk(n), es igual al cociente entre el número de


funciones que procesa el cauce, n, y el tiempo que tarda en procesarlas, es decir:

n n n
W k  n    (6)
T seg n TLI   n  1  t  k   n  1   t

La productividad máxima del cauce se obtendrá cuando n (el número de funciones


procesadas) sea lo suficientemente grande como para que el tiempo de latencia inicial,
TLI, sea despreciable frente a (n – 1) × t. Entonces,

1
W max  lim W k  n  (7)
n  t

y, por lo tanto, la productividad máxima de un cauce es la inversa del tiempo de etapa del
cauce. Cuanto más pequeño sea dicho tiempo, mayor será la máxima productividad del
cauce. Así, a medida que el diseño de un cauce tiene más etapas (con tiempos más
pequeños) mayor es la productividad máxima posible (tal y como ocurría con la ganancia
máxima).

Un procesador segmentado consta de varias etapas correspondientes a las distintas fases


por las que debe pasar la instrucción al ser procesada. Cada etapa puede procesar una
instrucción distinta en la fase correspondiente, por lo que el procesador puede aprovechar
el paralelismo entre instrucciones (ILP, Instruction Level Parallelism) al procesar
simultáneamente varias instrucciones (cada una en una fase distinta). La distribución de
etapas que tenga un procesador segmentado es una característica importante de su
microarquitectura y afecta de forma decisiva a sus prestaciones. Una posible distribución
de etapas sería la que incluye una etapa para captar la instrucción (IF, iniciales del inglés
Instruction Fetch) desde la memoria (usualmente desde la cache de instrucciones); otra
para decodificar la instrucción captada y, en su caso, acceder a los operandos que se
utilizarán en la operación codificada por la instrucción (ID/OF, de Instruction
Fetch / Operand Decode); otra para la ejecución de la operación (EX, de Execute); otra
para esperar que la memoria proporcione el dato necesario en una instrucción de carga o
para escribir el dato en una instrucción de almacenamiento (MEM); y finalmente una
etapa para almacenar los resultados en de las operaciones en los registros del procesador
(WB, de Write Back). En un procesador segmentado se pueden presentar problemas a
causa de las dependencias entre las instrucciones procesadas en el cauce. Si estas
dependencias ocasionan una reducción en el rendimiento del cauce al impedir que
termine una instrucción cada ciclo (una vez transcurrido el tiempo de latencia de inicio),
ocasionan un riesgo. Existen riesgos de datos, por ejemplo cuando una instrucción
necesita datos de otra instrucción y éstos no se han generado todavía; riesgos de control,
debidos a las interrupciones o a las instrucciones de salto, que afectan al flujo de
instrucciones que hay que ejecutar; y riesgos estructurales o colisiones, debidos a
recursos compartidos (por ejemplo buses de acceso a memoria) por etapas diferentes. El
efecto de los riesgos se puede paliar mediante técnicas hardware y/o técnicas software.
INSTRUCCIÓN 1 2 3 4 5 6 7 8 9
add r3, r3, r1 IF ID EX WB
sub r5, r5, r3 IF Stall ID EX WB
add r4, r4, r1 IF ID EX WB
sub r6, r6, r4 IF Stall ID EX WB

(a) Atascos producidos por los riesgos de datos.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9
add r3, r3, r1 IF ID EX WB
nop IF ID EX WB
sub r5, r5, r3 IF ID EX WB
add r4, r4, r1 IF ID EX WB
nop IF ID EX WB
sub r6, r6, r4 IF ID EX WB

(b) Solución a los riesgos de datos introduciendo instrucciones nop.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9
add r3, r3, r1 IF ID EX WB
add r4, r4, r1 IF ID EX WB
sub r5, r5, r3 IF ID EX WB
sub r6, r6, r4 IF ID EX WB

(c) Solución a los riesgos de datos reordenando el código.

Figura 1. Riesgos de datos, atascos y reorganización de código.

En la Figura 1 se considera un cauce con cuatro etapas. En la primera etapa se captan las
instrucciones, en la segunda se decodifican y se accede a los operandos almacenados en
el banco de registros del procesador, en la siguiente etapa se ejecutan las operaciones
codificadas en la instrucción, y en la última etapa se almacenan los resultados en el banco
de registros. En la Figura 1.a se muestra el efecto de un riesgo de datos entre las
instrucciones primera y segunda y de otro riesgo entre la instrucción tercera y la cuarta.
Como se puede ver, hay que retrasar la captación de los operandos en la segunda y cuarta
instrucciones hasta que se haya completado la ejecución de las instrucciones primera y
tercera, respectivamente. Se producen atascos (stalls) en el cauce que hacen que no se
pueda terminar el procesamiento de una instrucción por ciclo. En la figura se ha supuesto
que la escritura de los datos en el banco de registros (en la última etapa) se produce al
comienzo de la etapa (en el flanco de subida de reloj) y la lectura de los datos (en la
segunda etapa) se producen al final de la etapa (en el flanco de bajada del reloj), y por eso
sólo se debe esperar un ciclo en el atasco. La Figura 1.b muestra una posible forma de
evitar los riesgos introduciendo instrucciones de no-operar para retrasar la segunda y la
cuarta instrucción de la Figura 1.a con respecto a la primera y tercera, respectivamente.
En este caso se evita la necesidad de que exista algún procedimiento hardware que
gestione los atascos pero no se mejora la eficiencia del cauce. En la Figura 1.c se pone de
manifiesto cómo la reorganización de código puede evitar los riesgos de datos y mejorar
la eficiencia ya que en este caso, los retrasos entre instrucciones se consiguen
introduciendo instrucciones que tienen utilidad en el código. Este ejemplo pone de
manifiesto la importancia del trabajo del compilador en el rendimiento de los
procesadores segmentados. Por otra parte, también es posible solucionar este tipo de
riesgos mediante una solución hardware, incluyendo caminos de bypass o atajos que
adelanten el resultado de las unidades de ejecución a la siguiente instrucción.
INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12
W
add r4, r5, r2 IF ID EX
B
W
addi r5, r5, #1 IF ID EX
B
W
sub r3, r3, r1 IF ID EX
B
W
bnez r3, inicio IF ID EX
B
W
addi r2, r2, #1 IF ID EX
B
W
lw r6, 0(r2) IF ID EX
B
W
add r6, r3, r1 IF ID EX
B

(a) Riesgo de control introducido por el salto.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12
W
add r4, r5, r2 IF ID EX
B
W
addi r5, r5, #1 IF ID EX
B
W
sub r3, r3, r1 IF ID EX
B
W
bnez r3, inicio IF ID EX
B
W
nop IF ID EX
B
W
nop IF ID EX
B
W
addi r2, r2, #1 IF ID EX
B
W
lw r6, 0(r2) IF ID EX
B
add r6, r3, r1 IF ID EX WB

(b) Solución al riesgo de control introduciendo instrucciones nop.


INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12
W
sub r3, r3, r1 IF ID EX
B
W
bnez r3, inicio IF ID EX
B
W
add r4, r5, r2 IF ID EX
B
W
addi r5, r5, #1 IF ID EX
B
W
addi r2, r2, #1 IF ID EX
B
W
lw r6, 0(r2) IF ID EX
B
W
add r6, r3, r1 IF ID EX
B

(c) Solución al riesgo de control reordenando el código (salto retardado).

Figura 2. Riesgos de control y saltos retardados.

La Figura 2 se refiere a los riesgos de control provocados por instrucciones de salto


condicional. En la Figura 2.a, la instrucción bnez r3, inicio da lugar a un salto a la
dirección inicio según sea el resultado de la operación de la instrucción que le precede (en
este caso en función del valor del registro r3). Como la comprobación de la condición de
salto no se realiza hasta el final de la etapa de ejecución de la instrucción de salto, puede
que las dos instrucciones que se han introducido en el cauce detrás de la instrucción de
salto condicional no tengan que procesarse. Este fenómeno se conoce como riesgo de
control. Para evitar que instrucciones que no tienen luego que ejecutarse se introduzcan
en el cauce, se pueden introducir instrucciones de no-operar detrás de la instrucción de
salto, tal y como muestra la Figura 2.b. El número de instrucciones que hay que introducir
depende de la profundidad a la que se encuentre en el cauce la etapa donde se completa el
procesamiento del salto (y se determina la dirección a la que se produce el salto y si se
salta o no). En el caso de la Figura 2 hay que introducir dos instrucciones y se habla de
que hay dos huecos o slots detrás de la instrucción de salto. En la Figura 2.c se ilustra la
técnica de salto retardado. La idea consiste en introducir instrucciones que se deben
ejecutar antes de la instrucción de salto (y por lo tanto tienen que ejecutarse siempre,
independientemente de la condición de salto) en los huecos de la instrucción de salto. De
esta forma, aunque aparentemente parezca que se procesan después de la instrucción de
salto, en realidad terminan de ejecutarse antes. Para que esto se pueda hacer sin que
produzcan errores, como es lógico, no debe violarse ninguna dependencia entre las
instrucciones. Como se pone de manifiesto en la Figura 2.c, la técnica de salto retardado
mejora el rendimiento del cauce.
# 5 6 7 8 9 … 234 235 236 237 238 239 … 483 484 485 486 487 488 489 490
(5) IF ID EX MEM WB
(6) IF ID EX MEM
(7) IF ID EX
(8) IF ID
(9) IF

(5) IF ID EX MEM WB
(6) IF ID EX MEM WB
(7) IF ID EX MEM
(8) IF ID EX
(9) IF ID

(6) IF ID EX MEM WB
(7) IF ID EX MEM WB
(8) IF ID EX MEM WB
(9) IF ID EX MEM WB

(a) Tratamiento preciso de las excepciones.

# 2 3 4 5 6 … 245 246 247 248 249 250 … 474 475 476 477 478 479 480 481
(2) IF ID EX MEM WB
(3) IF ID EX MEM
(4) IF ID EX
(5) IF ID
(6) IF

(3) IF ID EX MEM WB
(4) IF ID EX MEM WB
(5) IF ID EX MEM
(6) IF ID EX
(7) IF ID

(5) IF ID EX MEM WB
(6) IF ID EX MEM WB
(7) IF ID EX MEM WB
(8) IF ID EX MEM WB

(b) Tratamiento impreciso de las excepciones.

Figura 3. Tratamiento de las excepciones.

El efecto de las interrupciones y las excepciones en el cauce es muy pernicioso dado que
dan lugar a una bifurcación en la secuencia de instrucciones a procesar. Además, en el
caso de las interrupciones no catastróficas (aquellas que no causan que el programa
concluya) se debe regresar a la secuencia que se estaba procesando antes de la
interrupción/excepción. La dificultad que plantea el procesamiento de las interrupciones
en un procesador segmentado se deriva de que hay varias instrucciones procesándose
simultáneamente y hay que identificar qué instrucción es la que ha dado lugar a una
excepción y qué instrucciones hay que ejecutar antes de ceder el control a la rutina de
gestión de la excepción (y por tanto qué instrucciones hay que ejecutar al retomar el
control de la secuencia que se ha interrumpido). Existen dos alternativas para el
procesamiento de interrupciones/excepciones en un procesador segmentado: las
interrupciones/excepciones precisas y las imprecisas. En el caso de las precisas todo
ocurre como si se tratase de un procesador no segmentado:

1. Se identifica la instrucción que ha dado lugar a la excepción (o la que se está


ejecutando cuando se recibe la interrupción).

2. Las instrucciones que están antes que ella en el código se terminan de procesar
antes de ceder el control a la rutina de gestión de la interrupción/excepción.

3. Tras procesar la interrupción/excepción, el programa continúa por la instrucción


que ha dado lugar a la excepción (o por la siguiente, según el tipo de excepción).

En la Figura 3.a se muestra un ejemplo de implementación de interrupciones/excepciones


precisas. La condición que marca que se ha producido una interrupción/excepción (y el
tipo de ésta) se evalúa en la última etapa del cauce (antes de que se escriban los
resultados de la instrucción) de manera que se determina de manera unívoca la
instrucción por donde se debe continuar después de la rutina de gestión (la propia
instrucción o la siguiente). Todas las instrucciones que preceden a la instrucción afectada
por la interrupción/interrupción se completan. En el caso de que se produzcan varias
excepciones, se atienden según las instrucciones van llegando a su última etapa, no en el
orden temporal en que tienen lugar. La Figura 3.b muestra un ejemplo de implementación
de interrupciones/excepciones imprecisas. En este caso las interrupciones/excepciones se
atienden en el orden temporal en que ocurren. Como se puede observar, se almacena la
dirección de la última instrucción que se había completado al atender la
interrupción/excepción (en el momento en que esta se produce). La secuencia de
instrucciones se reanuda en la instrucción siguiente a la que se había completado.

En un procesador, pueden incluirse unidades funcionales (multiplicadores, sumadores,


unidades aritmético-lógicas, etc.) segmentadas. En muchos casos estos cauces tienen una
estructura diferente de la estructura lineal en la que todas las etapas duran el mismo
tiempo y están encadenadas de forma que todas las funciones van pasando de una etapa a
la siguiente en el mismo orden y no vuelven a tener que utilizar etapas por las que ya han
pasado. Así, pueden tenerse cauces en los que

1. Puede haber etapas con distinto número de ciclos de duración.

2. Una función puede tener que reutilizar alguna de las etapas por las que ya ha
pasado.

3. Las etapas pueden ser utilizadas en orden diferente por distintas funciones (es el
caso de los cauces multifuncionales).

El problema que se plantea es el de diseñar la unidad de control que determina el


momento en que pueden introducirse funciones en el cauce para que no se produzcan
colisiones y se obtenga el máximo rendimiento del cauce. Para determinar la unidad de
control óptima se parte de la tabla de reservas del cauce, que indica la secuencia temporal
según la cual una función utiliza las etapas del cauce. Mediante esta tabla de reservas se
puede ver si se trata de un cauce unifuncional o multifuncional, si tiene etapas con
tiempos de duración diferentes, y si se reutilizan las etapas. En la Tabla 1 se muestra una
tabla de reservas para un cauce unifuncional correspondiente a un cauce con 5 etapas (S1,
…, S5) en el que hay etapas que se reutilizan y etapas de distinta duración. Una función
tarda 9 ciclos en completarse en ese cauce. Es decir, el tiempo de latencia de inicio es 9.

ETAPA 1 2 3 4 5 6 7 8 9
S1 X X
S2 X X X
S3 X
S4 X X
S5 X X

Tabla 1. Tabla de reservas y latencias prohibidas.

Dada la tabla de reservas se determina la lista de latencias prohibidas, es decir los


intervalos de tiempo entre los que las etapas del cauce se vuelven a reutilizar. Para ello se
van considerando las etapas una a una y se incluyen en la lista de latencias todos los
intervalos de tiempo entre todas las parejas de casillas marcadas en cada etapa. Por
ejemplo, la etapa S2 introduce tres latencias prohibidas en la lista (1, 5, y 6),
correspondiendo a los intervalos de separación para cada una de las tres parejas de
casillas marcadas que existen. El conjunto de latencias prohibidas para esta tabla de
reservas es F = {1, 5, 6, 8}. A partir de la lista de latencias prohibidas se construye el
vector de colisiones constituido por un número de componentes igual al máximo número
incluido en la lista de latencias prohibidas y cuyos componentes se numeran empezando
en uno desde la derecha. Un componente es igual a 1 si su índice está incluido en la lista
de latencias prohibidas y es 0 en caso contrario. En el ejemplo que consideramos, el
vector de colisiones es C = (10110001). El vector de colisiones representa los intervalos
de tiempo en los que, tras introducir una función en el cauce vacío, no se pueden
introducir nuevas funciones debido a que causarían colisiones en el cauce.

A partir del vector de colisiones se puede construir un diagrama de estados cuyos nodos
indican las latencias prohibidas del cauce en para una determinada combinación de
funciones introducidas en el mismo y cuyos arcos se marcan con los intervalos de tiempo
que hay que esperar para pasar del estado de partida del arco al estado al que apunta al
introducir una nueva función en el cauce. En la Figura 4 se muestra el procedimiento para
determinar los estados siguientes a uno dado. Los arcos que salen del estado de partida
están marcados con el número de intervalos de tiempo que se espera para introducir la
nueva función. Como se ve, todos corresponden a ceros en el estado de partida (no son
latencias prohibidas). El estado de partida se desplaza un número de componentes hacia
la derecha, igual al número de ciclos que han transcurrido y se introduce ese número de
ceros por la izquierda. Después se debe hacer la unión bit a bit con el vector de
colisiones, para contabilizar las latencias prohibidas que introduce la nueva función que
se ha introducido en el cauce. Con todas las posibles formas de salir de cada uno de los
estados se construye un diagrama como el que se muestra en la Figura 5 para la tabla de
reservas de la Tabla 1.

En ese diagrama, los estados de cada ciclo indican qué latencias están prohibidas en
función de las operaciones que haya actualmente en el cauce, y las etiquetas de los arcos
el número ce ciclos que tienen que transcurrir para aceptar una nueva operación. Con
ayuda de este tipo de diagramas se pueden determinar ciclos de latencias válidas para
acepar operaciones, y con estos ciclos, se podrá construir el planificador que gestione el
cauce de forma óptima. Un ciclo no es más que un camino cíclico que pasa por uno o
varios estados del diagrama y que comienza y termina en el mismo estado. La latencia
total del ciclo será la suma de las latencias de cada uno de los arcos que lo componen, y
su latencia media, LM, será el cociente su latencia total y el número de arcos que lo
compongan. Si la unidad de control del cauce funciona de forma que se introducen
instrucciones según un ciclo determinado, la productividad máxima (expresada en
funciones u operaciones por ciclo) que puede conseguir es el cociente entre el número de
instrucciones y el tiempo correspondiente a la latencia total del ciclo, o lo que es lo
mismo W = 1 / LM. Por lo tanto, nos interesa identificar el ciclo de mínima latencia
media, (MLM), ya que la unidad de control diseñada según este ciclo nos proporcionará
la productividad máxima para el cauce, Wmax = 1 / MLM.
10110111 Estado actual
9+
10110111 Desplazamos a la derecha el
7 00001011
número de bits que indique el
4 arcoa tabla de reservas de
10110001 Estado inicial (vector de
10111011 colisiones)
10111011 Unión bit a bit

Figura 4. Generación del diagrama de estados a partir de la tabla de reservas de la Tabla 1.

7, 9+

10110001
7, 9+
7, 9+ 3 2 7, 9+ 7, 9+
10110001 10110001
3
4 2
10110001 10110001

Figura 5. Diagrama de estados para el cauce de la tabla de reservas de la Tabla 1.

En la Figura 5 se han resaltado dos de los ciclos posibles. El ciclo de la izquierda tiene
una latencia media LM = (7 + 2 + 2) / 3 = 3.67, mientras que para el ciclo de la derecha,
LM = (3 + 4) / 2 = 3.5. Por lo tanto, una unidad de control diseñada según el ciclo de la
derecha es mejor que si se diseña a partir del ciclo de la izquierda.

Los dos ciclos que se han resaltado reciben el nombre de ciclos avariciosos porque se
construyen saliendo de cada estado por el arco que implica esperar el menor tiempo para
introducir una nueva función en el cauce. Identificar el mejor de los ciclos avariciosos y
realizar el diseño de la unidad de control según el mismo es una estrategia aproximada
para obtener la mejor unidad de control que puede ser útil en el caso de diagramas de
flujo complicados (con muchos ciclos de funcionamiento posibles).

Una descripción más completa del procedimiento de diseño de la unidad de control en


cauces no lineales, así como el procedimiento de diseño para el caso de un cauce
multifuncional, puede encontrarse en [ORT05].
Problemas
1. Un circuito que implementaba una operación en T = 450 ns se ha segmentado mediante
un cauce lineal con cuatro etapas de duración t1 = 100 ns, t2 = 125 ns, t3 = 125 ns, y
t4 = 100 ns respectivamente, separadas por un registro de acoplo que introduce un retardo
d = 25 ns.

a) ¿Cuál es la máxima ganancia de velocidad posible?

b) ¿Cuál es la productividad máxima del cauce?

c) ¿A partir de qué número de operaciones ejecutadas se consigue una productividad


igual al 90% de la productividad máxima?

Solución
El tiempo de ejecución de n operaciones en el circuito original se calcula como:

T orig  n  n  T  450n ns

Para calcular el tiempo de ejecución en el circuito segmentado es necesario conocer su tiempo de


ciclo, que se obtiene mediante la siguiente expresión

t  max t 1 , t 2 , t 3 , t 4   d  max100,125,125,100  25  150 ns

Una vez calculado el tiempo de ciclo, el tiempo de ejecución de n operaciones en el circuito


segmentado se calcula usando la siguiente expresión:

Tseg  n   k   n  1   t   4   n  1   150  150n  450 ns

Conocidos los tiempos de ejecución en los circuitos original y segmentado, se puede calcula la
ganancia en velocidad que se obtiene usando el circuito segmentado con la siguiente expresión:

T orig  n 450n 3n
S  n   
T seg  n 150n  450 n  3

Para calcular la productividad del cauce aplicaremos la expresión:

n n n  109
W  n   operaciones/ns  operaciones/s
T seg  n 150n  450 150n  450

La ganancia y la productividad máximas del cauce se obtienen aplicando el límite cuando n → ∞ a
la ganancia y a la productividad respectivamente:

3n
S max  lim S  n  lim 3
n n3
n

n  109 
W max  limW  n  lim  6.6  106 operaciones/s
n n  150n  450

Por último, el valor de n para el que se alcanza un 90% de la productividad se calcula despejando
n a partir de la igualdad:
n  109 
W  n   0.9  W max  6.6  106 operaciones/s
150n  450

Lo que da como resultado n = 27.

2. Considere que el fragmento de código siguiente:

lw r1, 0x1ac ; (1)


lw r2, 0x1fc ; (2)
add r3, r0, r0 ; (3)
mult r4, r2, r1 ; (4)
add r3, r3, r4 ; (5)
add r5, r0, 0x1ac ; (6)
add r6, r0, 0x1fc ; (7)
sub r5, r5, #4 ; (8)
sub r6, r6, #4 ; (9)
sw (r5), r3 ; (10)
sw (r6), r4 ; (11)

se ejecuta en un procesador segmentado con etapas IF (captación de instrucciones), ID


(Decodificación de Instrucciones/Captación de Operando), EX (Ejecución de
instrucción), MEM (ciclo de acceso a memoria/espera), OS (Almacenamiento de
resultados en el banco de registros). En la etapa de ejecución, una instrucción puede
utilizar una ALU para sumas/restas con enteros y operaciones lógicas (1 ciclo de
latencia), un sumador para operaciones en coma flotante (2 ciclos), un multiplicador (5
ciclos de latencia), o un divisor (19 ciclos de latencia)

a) ¿Cuántos ciclos tarda en ejecutarse el programa anterior suponiendo que en el


procesador existen caminos de bypass que permiten el paso directo de resultados
desde las salidas de los circuitos aritméticos a sus entradas? Indique las
dependencias de datos entre instrucciones que podrían tener efecto en el
rendimiento del cauce si no hubiese caminos de bypass.

b) ¿Cómo se podrían reorganizar las instrucciones para que los riesgos de tipo RAW
no tuvieran ningún efecto? (Considere que intervienen los caminos de bypass).

Solución
La Figura 6 muestra una traza de la ejecución del fragmento de código del enunciado. En el
diagrama se observa que se pierden 4 ciclos en los que no termina ninguna instrucción debido a la
dependencia de tipo RAW que existe entre las instrucciones (4) y (5) respecto al registro r4,
debido a que el resultado de la multiplicación (4) es el operando de la instrucción (5), y la
multiplicación tarda cinco ciclos en completarse. Por ello, aunque haya varias unidades
funcionales, que podrían aprovecharse en paralelo en este caso (hay un multiplicador y un
sumador en el cauce), la suma debe esperar a que termine la multiplicación.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 19
0 1 2 3 4 5 6 7 8
M
I E O
lw r1, 0x1ac IF
D X
E
S
M
M
I E O
lw r2, 0x1fc IF
D X
E
S
M
M
I E O
add r3, r0, r0 IF
D X
E
S
M
M
I O
mult r4, r2, r1 IF
D
EX E
S
M
M
I E O
add r3, r3, r4 IF
D
Stall
X
E
S
M
M
I E O
add r5, r0, 0x1ac IF Stall
D X
E
S
M
M
I E O
add r6, r0, 0x1fc Stall IF
D X
E
S
M
M
I E O
sub r5, r5, #4 IF
D X
E
S
M
M
I E O
sub r6, r6, #4 IF
D X
E
S
M
M
I E O
sw (r5), r3 IF
D X
E
S
M
M
I E O
sw (r6), r4 IF
D X
E
S
M

Figura 6. Traza de la ejecución del código del problema 2.

Si el tiempo de latencia del multiplicador fuese 1 ciclo, entonces no se perdería ningún ciclo,
gracias a que, con los caminos de bypass, los riesgos de tipo RAW no tienen ningún efecto. En
este caso, el programa tardaría en ejecutarse 15 ciclos, el mínimo posible:

T  n  TLI   n  1  5   11  1  15 ciclos

Teniendo en cuenta que en este cauce las instrucciones terminan de forma ordenada, las
dependencias de datos que pueden afectar son las de tipo RAW. Las WAW y WAR no dan
problemas porque al escribirse los resultados en el banco de registros sólo en la última etapa, se
puede estar seguro que en el momento en que escribe una instrucción, todas las instrucciones
anteriores han realizado sus escrituras, y por supuesto, sus lecturas.

Para contabilizar las dependencias de tipo RAW se toman aquellas instrucciones que estén
separadas por menos (estrictamente) de dos instrucciones, ya que son las que podrían estar
simultáneamente en el cauce, y en donde la instrucción que entra en primer lugar en el cauce
puede escribir sus resultados después de que las que le siguen vayan a leerlos (no se respetaría que
la lectura ha de ser posterior a la escritura). Aquí se está considerando que la escritura en el banco
de registros en la etapa OS se realiza al comienzo del ciclo, y que la lectura en dicho banco se hace
al final del ciclo, ya que si no fuera así, las instrucciones separadas por otras dos instrucciones sí
que podrían ocasionar pérdidas de ciclo en los riesgos RAW. Teniendo esto en cuenta, las
dependencias de tipo RAW que existen son:

 Instrucción (2) y (4) en r2

 Instrucción (3) y (5) en r3

 Instrucción (4) y (5) en r4

 Instrucción (6) y (8) en r5

 Instrucción (7) y (9) en r6

 Instrucción (8) y (10) en r5

 Instrucción (9) y (11) en r6


Considerando que no existieran caminos de bypass (y que el tiempo de la multiplicación es de 1
ciclo) se puede comprobar que el tiempo que tarda el programa es de 20 ciclos, es decir 5 más que
el tiempo mínimo determinado antes. Estos cinco ciclos que se pierden se explican de la siguiente
forma:

 El RAW (2)-(4) ocasiona 1 ciclo perdido. Este ciclo perdido retrasa la captación
de (5) respecto a (3) y el RAW (3)-(5) no ocasiona pérdidas de ciclos.

 El RAW (4)-(5) ocasiona una pérdida de 2 ciclos.

 El RAW (6)-(8) ocasiona 1 ciclo perdido pero al retrasar la captación de (9)


respecto a (7) hace que el RAW (7)-(9) no afecte.

 Igual pasa con el RAW (8)-(10), éste ocasiona la pérdida de 1 ciclo, pero al
retrasar la captación de (11), hace que desaparezca el efecto del RAW (9)-(11).

En cuanto al segundo apartado del problema, pueden existir distintas formas de reorganizar el
código para que los riesgos de tipo RAW no tengan efecto. El objetivo es separar las instrucciones
entre las que existían riesgos de tipo RAW que producían pérdidas de ciclos utilizando otras
instrucciones, evitando que se introduzcan nuevos riesgos problemáticos, y lógicamente, sin
cambiar el resultado del programa.

En el caso que nos ocupa, se perdían 4 ciclos por el riesgo de tipo RAW existente entre las
instrucciones (4) de multiplicación (que duraba 5 ciclos) y la (5) de suma. Para resolver el
problema, una posibilidad es retrasar la instrucción (5), introduciendo entre ella y la instrucción (4)
las instrucciones (6), (7), (8), y (9) ya que no dependen de la (5). De esta forma, la instrucción (10)
pasaría a estar a continuación de la (5) habría un riesgo de tipo RAW (por r3). Si se utilizan
caminos de bypass no se perderían ciclos, pero, en cualquier caso, es posible reordenar (10) y (11)
para que haya más separación entre (5) y (10). De esta forma, el código quedaría:

lw r1, 0x1ac ; (1)


lw r2, 0x1fc ; (2)
add r3, r0, r0 ; (3)
mult r4, r2, r1 ; (4)
add r5, r0, 0x1ac ; (6)
add r6, r0, 0x1fc ; (7)
sub r5, r5, #4 ; (8)
sub r6, r6, #4 ; (9)
add r3, r3, r4 ; (5)
sw (r6), r4 ; (11)
sw (r5), r3 ; (10)

Al ejecutarlo, se puede observar que se necesitan 16 ciclos. Es decir, uno más que el valor mínimo,
dado que aparece un riesgo estructural. En concreto, en el ciclo 11 intentan acceder a la etapa de
memoria las instrucciones (4) y (9), y dado que sólo puede hacer una instrucción en cada etapa
simultáneamente, la instrucción (9) y siguientes deben esperar un ciclo. Esta situación se puede
comprobar en la Figura 7.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
lw r1, 0x1ac IF ID EX MEM OS
lw r2, 0x1fc IF ID EX MEM OS
add r3, r0, r0 IF ID EX MEM OS
mult r4, r2, r1 IF ID EX MEM OS
add r5, r0, 0x1ac IF ID EX MEM OS
add r6, r0, 0x1fc IF ID EX MEM OS
sub r5, r5, #4 IF ID EX MEM OS
sub r6, r6, #4 IF ID EX Stall MEM OS
add r3, r3, r4 IF ID Stall EX MEM OS
sw (r6), r4 IF Stall ID EX MEM OS
sw (r5), r3 Stall IF ID EX MEM OS

Figura 7. Traza de la ejecución del código optimizado del problema 2.

Si se realizara la simulación suponiendo que no existen caminos de bypass se podría observar que
el tiempo de ejecución subiría a 20 ciclos. Se podría reducir un ciclo (similares prestaciones que en
la situación inicial del problema anterior, con el código sin reordenar y sin caminos de bypass) si
se realiza la simulación con el fragmento de código reordenado como se indica a continuación:

lw r1, 0x1ac ; (1)


lw r2, 0x1fc ; (2)
add r5, r0, 0x1ac ; (6)
mult r4, r2, r1 ; (4)
add r6, r0, 0x1fc ; (7)
add r3, r0, r0 ; (3)
sub r5, r5, #4 ; (8)
sub r6, r6, #4 ; (9)
add r3, r3, r4 ; (5)
sw (r6), r4 ; (11)
sw (r5), r3 ; (10)

3. Realice un programa para calcular la expresión aX + Y, donde a es un escalar y X e Y son


dos vectores cuyos componentes se encuentran almacenados en memoria en posiciones
consecutivas a partir de dos direcciones establecidas. Además, tanto a como las
componentes de X e Y son números de doble precisión.

a) Suponiendo un cauce con las etapas IF, ID, EX, MEM, WB, en el que la
multiplicación tarda 5 ciclos y la suma de números en coma flotante tarda 2, y
hay un multiplicador y un sumador que pueden trabajar en paralelo, estime el
número de ciclos que se necesitarían, y aplique alguna técnica que permita
reducir los atascos (stalls) para este programa.

b) Utilice la técnica de segmentación software (software pipelining) para mejorar las


prestaciones del programa (sin utilizar desenrollado de bucle).

Solución
Un programa posible para realizar aX + Y es el que se muestra a continuación

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0, ry ; (4)
loop: ld f2, 0(r8) ; (5)
multd f2, f0, f2 ; (6)
ld f4, 0(r12) ; (7)
addd f4, f2, f4 ; (8)
sd 0(r12), f4 ; (9)
addi r8, r8, #8 ; (10)
addi r12, r12, #8 ; (11)
sub r20, r4, r8 ; (12)
bnez r20, loop ; (13)
nop ; (14)
donde rx y ry son las direcciones de memoria a partir de las que se encuentran almacenados los
componentes de los vectores. En el programa se ha fijado el número de componentes N = 64, por
lo que el número de bytes que componen el vector X (y lo mismo el vector Y) es igual a
64 × 8 = 512 bytes. De esta forma, r8 apunta al comienzo del vector X tras la instrucción (2); r4
apunta al final del vector X (primer byte no ocupado por las componentes del vector) tras la
instrucción (3); y r12 apunta al comienzo de Y tras la instrucción (4). En cada iteración se va
incrementando el valor de r8 y r12 en 8, ya que en cada iteración se hacen las operaciones
correspondientes a un componente, y cada componente tiene 64 bits (8 bytes). Para comprobar el
final de las iteraciones se comparan r8 y r4.

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
ld f0, a IF ID EX MEM WB
add r8, r0, rx IF ID EX MEM WB
addi r4, r8, #512 IF ID EX MEM WB
add r12, r0, ry IF ID EX MEM WB
ld f2, 0(r8) IF ID EX MEM WB
multd f2, f0, f2 IF ID Stall EX MEM WB
ld f4, 0(r12) IF Stall ID EX MEM WB
addd f4, f2, f4 Stall IF ID Stall EX MEM WB
sd 0(r12), f4 IF Stall ID Stall EX MEM WB
addi r8, r8, #8 Stall IF Stall ID EX MEM WB
addi r12, r12, #8 Stall IF ID EX MEM WB
sub r20, r4, r8 IF ID EX MEM WB
bnez r20, loop IF Stall ID
nop Stall IF ID Abort
ld f2, 0(r8) IF ID EX MEM
multd f2, f0, f2 IF ID Stall
ld f4, 0(r12) IF Stall

Figura 8. Traza de la ejecución del código del problema 3.

Como indica la Figura 8, existen riesgos de tipo RAW que ocasionan pérdidas de ciclos, a pesar de
que se considere que se dispone de caminos de bypass:

 RAW entre la instrucción (5) y la (6): se pierde un ciclo debido a que la


multiplicación indicada en (6) no se puede empezar hasta que no se haya captado
de memoria el dato necesario, tras la etapa MEM de (5).

 RAW entre la instrucción (6) y (8), ya que no puede empezar la suma de (8)
hasta que no se haya terminado la multiplicación de (6)

 RAW entre (8) y (9), ya que hasta que no haya terminado la suma de (8) no
podría empezar la ejecución de (9).

 RAW entre las instrucciones (12) y (13), ya que se supone que, como en el caso
del procesador DLX, en la fase de decodificación de la instrucción de salto (13)
se debe haber ejecutado la instrucción anterior (12).

Como se puede comprobar en la en la Figura 8, aún adelantando el procesamiento del salto a la


etapa de decodificación, se introduce una instrucción incorrectamente en el cauce (la instrucción
14), que se debe anular una vez que se ejecuta el salto. También se puede observar que en el ciclo
10 se están ejecutando dos instrucciones simultáneamente, (6) y (7). Esto es posible porque, como
dice el enunciado, además de la ALU, el procesador dispone de un multiplicador que puede operar
en paralelo. Por otra parte, la figura también muestra que la instrucción (7) puede terminar antes
que la (6), dado que no existen dependencias entre ellas y no afecta al resultado.
El tiempo de ejecución de una iteración es de 16 ciclos (desde que se capta la instrucción (5) hasta
que se vuelve a captar, tras la instrucción de salto). Así, si se realizan N iteraciones, el tiempo
consumido por el programa sería 16 × N, pero a este número hay que sumar, los 4 ciclos
correspondientes a las instrucciones que están fuera del bucle (al principio), más los tres ciclos que
hay que esperar para que termine la última instrucción del programa (14). Por lo tanto, este
programa tardaría:

T  N   4  16N  3  16N  7

Una forma de mejorar las prestaciones del programa es desenrollar el bucle. En el código que se
muestra a continuación, se ha aplicado esta técnica de forma directa. Es decir, se duplican las
instrucciones de una iteración del bucle sin desenrollar, excepto la instrucción de resta utilizada
para determinar el final de las iteraciones.

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0 ,ry ; (4)
loop: ld f2, 0(r8) ; (5)
multd f2, f0, f2 ; (6)
ld f4, 0(r12) ; (7)
addd f4, f2, f4 ; (8)
sd 0(r12), f4 ; (9)
addi r8, r8, #8 ; (10)
addi r12, r12, #8 ; (11)
ld f6, 0(r8) ; (5’)
multd f6, f0, f6 ; (6’)
ld f8, 0(r12) ; (7’)
addd f8, f6, f8 ; (8’)
sd 0(r12), f8 ; (9’)
addi r8, r8, #8 ; (10’)
addi r12, r12, #8 ; (11’)
sub r20, r4, r8 ; (12)
bnez r20, loop ; (13)
nop ; (14)

En el código anterior los números de instrucciones (5’),..., (11’) designan a las instrucciones
correspondientes a (5),...., (11) en una iteración del bucle.

Para determinar el tiempo tardaría en ejecutarse el programa en este caso, se puede utilizar el
diagrama de etapas y tiempos utilizado anteriormente. En este caso, el tiempo transcurrido entre
que se capta la instrucción (5) y se capta la instrucción (12) es igual a 12 ciclos (17 - 5 = 12).
Como una iteración tiene dos secuencias similares de instrucciones (5) - (11) y (5´) - (11’), se
tendrán que contabilizar 2 × 12 ciclos = 24 ciclos para la secuencia (5),…, (11), (5’),…, (11’).
Además, habrá que añadir cuatro ciclos correspondientes al ciclo de captación de la instrucción de
resta (para el control del final del bucle), y a los tres ciclos asociados a la instrucción de salto. En
el esquema de etapas y ciclos presentado, estos cuatro ciclos son los 17, 18, 19 y 20.

Así pues, cada iteración necesita 28 ciclos (en lugar de los 16 del bucle sin desenrollar), pero hay
que ejecutar la mitad de iteraciones (N / 2). Por lo tanto, si a eso se unen los cuatro ciclos de las
instrucciones iniciales, y los tres que hay que esperar para que termine la ejecución de la última
instrucción, el tiempo necesario (suponiendo N par) es:

N
T desenrollado  N   4  28  3  14N  7
2
De esta forma, se reduce el tiempo de ejecución en 2N ciclos con respecto al bucle inicial. Se
puede reducir algo más el tiempo de ejecución si se reordenan las instrucciones en el código
correspondiente al bucle desenrollado (aprovechando que en cada iteración se tienen más
instrucciones para reordenar). Una posible forma de reordenar las instrucciones se muestra a
continuación.

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0, ry ; (4)
loop: ld f2, 0(r8) ; (5)
ld f4, 0(r12) ; (7)
multd f2, f0, f2 ; (6)
ld f6, 8(r8) ; (5’)
ld f8, 8(r12) ; (7’)
addd f4, f2, f4 ; (8)
multd f6, f0, f6 ; (6’)
sd 0(r12), f4 ; (9)
addi r8, r8, #16 ; (10’)
sub r20, r4, r8 ; (12)
addd f8, f6, f8 ; (8’)
sd 8(r12), f8 ; (9’)
addi r12, r12, #16 ; (11’)
bnez r20, loop ; (13)
nop ; (14)

Los cambios realizados con respecto al primer bucle desenrollado buscan distanciar en el código
aquellas instrucciones entre las que existían riesgos ocasionaban pérdidas de ciclos. Así, para
evitar el riesgo RAW entre las instrucciones (5) y (6), se ha introducido entre ellas la instrucción
(7). Entre las instrucciones (6) y (8) se han introducido dos instrucciones sin dependencias entre
ellas ni con (6) u (8) por lo que se reduce en uno, el número de ciclos que se pierden en la
situación anterior, ya que entre (6) y (8) hay dos instrucciones en lugar de una. Igualmente, entre
(6’) y (8’) se han introducido tres instrucciones, y se reducen en dos, el número de ciclos que se
perdían por las dependencias entre la multiplicación y la suma. Sin embargo, al quedar las
instrucciones (9’) y (11’), una al lado de otra, se produce una pérdida de un ciclo por la
dependencia en r12.

Así pues, en cada iteración se pierden cuatro ciclos debido a dependencias, con respecto al caso
ideal en el que cada instrucción tarda un ciclo. Así pues, como en la iteración hay 14 instrucciones
y se pierden 4 ciclos, el tiempo de cada iteración es 14 + 4 = 18, más un ciclo que se pierde
siempre después de la instrucción de salto. En total tenemos que cada iteración necesita 19 ciclos,
y por tanto (teniendo en cuenta que N es par):

N
T desenrollado2  N   4  19  3  9.5N  7
2

De esta forma, se ganan 4.5 ciclos por iteración con respecto al desenrollado anterior, y 6.5 ciclos
por iteración con respecto a la situación inicial.

En cuanto al segundo apartado del problema, el siguiente código muestra una forma posible de
aplicar segmentación software al programa original.

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0, ry ; (4)
loop0: ld f2, 0(r8) ; (5)
multd f2, f0, f2 ; (6)
ld f4, 0(r12) ; (7)
ld f6, 8(r8) ; (8)
ld f8, 8(r12) ; (9)
addd f4, f2, f4 ; (10)
loop: sd 0(r12), f4 ; (11)
multd f6, f0, f6 ; (12)
addi r8, r8, #8 ; (13)
addi r12, r12, #8 ; (14)
addd f4, f6, f8 ; (15)
ld f6, 8(r8) ; (16)
ld f8, 8(r12) ; (17)
sub r20, r4, r8 ; (18)
bnez r20, loop ; (19)
nop ; (20)

En el programa, las instrucciones (1) – (4) coinciden con las del programa inicial, y permiten que
r8 apunte al comienzo del vector X, r12 al comienzo del vector Y, r4 al primer byte después del
vector X, y f0 se carga con el dato a.

Después, el grupo de instrucciones (5) – (10), que van desde la posición loop0 hasta la loop se
hacen las cargas de la primera iteración, los cálculos de la primera iteración y las cargas de la
segunda iteración, dejando los registros preparados para que comience el cuerpo del bucle
segmentado. La instrucción (5) carga f2 con la primera componente de X, y se multiplica por a en
la instrucción (6). La instrucción (7) carga la primera componente de Y en f4, y las instrucciones
(8) y (9) cargan las segundas componentes de X e Y en f6 y f8, respectivamente. La instrucción
(10) realiza la suma de la primera componente de aX y la primera de Y. Como se puede ver, se han
introducido las instrucciones (8), y (9) entre esta instrucción y la instrucción (7) donde se carga la
primera componente de Y, para retrasar la ejecución de (10) con respecto a (6) y a (7), y reducir el
efecto de los riesgos RAW.

Una vez dentro del bucle:

 En la primera iteración, la instrucción (11) realiza el almacenamiento del


resultado de la primera componente. Así, en la iteración i, se almacenará el
resultado de correspondiente a la componente i.

 En la primera iteración, las instrucciones (12) y (15) realizan los cálculos con los
datos correspondientes a la segunda componente, a los que se ha accedido
mediante las instrucciones (8) y (9). Por lo tanto, en la iteración i del bucle, se
realizan las operaciones con las componentes i + 1.

 En la primera iteración, las instrucciones (16) y (17) captan los datos


correspondientes a las terceras componentes de los vectores X e Y. Es decir, que
en la iteración i, se cargan las componentes i + 2. Para ello, en (16) y (17) se
accede a la posición r8 + 8 y a la r12 + 8 respectivamente, puesto que en las
instrucciones (13) y (14) ya se había incrementado r8 y r12 en 8.

 En la última iteración, (11) almacena la última componente del resultado; (12) y


(15) hacen un cálculo innecesario con datos que se captaron en la iteración
anterior por (16) y (17); y (16) y (17) captan datos innecesarios. Dado que el
efecto de (12) y (15) en la última iteración, y los de (16) y (17) en la última y en
la penúltima no afectan al resultado del programa, no hace falta deshacer su
efecto o evitar que se ejecuten.

 En el bucle, se han reordenado las instrucciones para evitar el efecto de los


riesgos RAW. Así, (12) y (15) están lo más separadas posible, y lo mismo pasa
con (13) y (16), y con (14) y (17).
El tiempo de ejecución del programa en este caso se puede estimar si se tienen en cuenta las
instrucciones entre las que hay dependencias de tipo RAW y su separación. Así:

 Entre la instrucción (5) y (6) hay un RAW que dará lugar a un ciclo perdido por
que cuando empieza la multiplicación todavía no se ha terminado el acceso a
memoria: 1 ciclo perdido

 Entre la (6) y la (10) también hay un RAW, y puesto que la multiplicación que
se realiza en la (6) tarda (5) ciclos, se perderá un ciclo, ya que se introducen 3
instrucciones entre la (6) y la (10): 1 ciclo perdido

 Entre la (1) y la (11) hay un RAW, y puesto que la suma tarda dos ciclos, se
perderá un ciclo (suponiendo que hay adelantamiento, o caminos de bypass para
llevar el resultado de la suma directamente al registro para el almacenamiento en
memoria): 1 ciclo perdido

 Entre la (12) y la (15) hay un RAW, igual que entre la (5) y la (6). No obstante,
como ahora sólo se introducen dos instrucciones entre ellas, se perderán dos
ciclos: 2 ciclos perdidos

 Al terminar (15) se pierde un ciclo, debido ya que la suma tarda dos ciclos y
habrá un riesgo estructural con alguna de las instrucciones que la siguen para
acceder a la etapa MEM: 1 ciclo perdido.

 Entre la (18) y la (19) se pierde otro ciclo debido al riesgo RAW que existe entre
estas instrucciones, y a que se necesita haber ejecutado (terminado la etapa EX)
la instrucción (18) antes de que (19) esté en su etapa de decodificación (ID): 1
ciclo perdido.

Teniendo en cuenta el efecto de los riesgos en los ciclos perdidos tenemos que el tiempo del
programa anterior se puede calcular como sigue:

4. Tiempo de las instrucciones (1) - (10) y primera iteración del bucle (11) - (19):

4 (primeras cuatro etapas de la primera instrucción) +

19 (número de instrucciones) +

7 (ciclos perdidos) –

3 (la primera instrucción de la segunda iteración empieza justo después de la


etapa ID de la instrucción de salto (19)) = 27 ciclos

5. Restantes 63 iteraciones de (11) - (19)

4 (primeras cuatro etapas de la primera instrucción del bucle) +

9 (número de instrucciones) +

4 (ciclos perdidos por las dependencias entre instrucciones dentro del bucle)

3 (la primera instrucción de la segunda iteración empieza justo después de la


etapa ID de la instrucción de salto (19)) = 14 ciclos × 63
(iteraciones) = 882

6. Ciclos hasta que termina la última instrucción: 3 (o 4 si se cuenta la instrucción nop).


Sumando 1), 2), y 3) se tiene un total del 912 ciclos, que supone una reducción de
1031 - 912 = 119 ciclos (11.5% de reducción) con respecto a la situación inicial del problema.

En función de N, se tendría un tiempo de ejecución de:

Tseg.soft  N   16  14N

Es decir, que la reducción de tiempo sería aproximadamente igual a 2N, con respecto a la situación
inicial (para valores de N suficientemente grandes). Como se ve, se tiene una complejidad similar
a la que se obtenía con el desenrollado.

En cualquier caso, es posible obtener mejores prestaciones todavía si se reorganizan algo más las
instrucciones para evitar la pérdida de algunos ciclos por los riesgos existentes. Por ejemplo, en el
programa que se muestra a continuación:

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
ld f2, 0(r8) ; (5)
addi r4, r8, #512 ; (3)
multd f2, f0, f2 ; (6)
add r12, r0, ry ; (4)
ld f4, 0(r12) ; (7)
ld f6, 8(r8) ; (8)
ld f8, 8(r12) ; (9)
addd f4, f2, f4 ; (10)
loop: multd f6, f0, f6 ; (12)
sd 0(r12), f4 ; (11)
addi r8, r8, #8 ; (13)
addi r12, r12, #8 ; (14)
addd f4, f6, f8 ; (15)
sub r20, r4, r8 ; (18)
ld f6, 8(r8) ; (16)
ld f8, 8(r12) ; (17)
bnez r20, loop ; (19)
nop ; (20)

Después de la reordenación realizada, sólo se pierden dos ciclos por iteración: uno debido a
dependencia RAW entre (12) y (15), entre las que se han introducido 3 instrucciones, y otro ciclo
debido a un riesgo estructural al terminar (15) puesto que las instrucciones que la siguen sólo
necesitan un ciclo en la etapa EX.

Según esto, el tiempo de ejecución es:

T(64) = 22 (primera ejecución de (1) – (19)) +

12 * 63 (63 iteraciones restantes) + 3 (hasta terminar) = 781

que supone casi un 25% menos de tiempo que en la situación inicial.

Para N, se tendrá T(N) = 13 + 12 × N, con lo que se consigue una reducción de 4 × N (para N
elevados) con respecto a la situación inicial.

4. En el problema anterior se ha supuesto que el procesador abortaba cualquier instrucción


que se captase después de una instrucción de salto. Esta situación obliga a que se tenga
que introducir una instrucción sin utilidad para el programa (instrucciones de no-operar).
Indique qué cambio haría en el código inicial si el procesador captara y ejecutara
normalmente la instrucción que sigue a una instrucción de salto condicional.
Solución
Una forma posible de aprovechar que las instrucciones que siguen a una instrucción de salto se
ejecuten normalmente es poner la primera instrucción de una iteración (la instrucción (5)) después
de la instrucción de salto. Por supuesto, en el caso de que no se produzca el salto también se
ejecutará, y habrá que estudiar si esto tiene efectos en la semántica del programa.

En el ejemplo que se muestra a continuación, la instrucción de carga de f2 con r8 se ejecutaría


innecesariamente al final del programa, modificando f2. Habría que ver si esto afecta al resto de la
aplicación.

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0, ry ; (4)
ld f2, 0(r8) ; (5)
loop: multd f2, f0, f2 ; (6)
ld f4, 0(r12) ; (7)
addd f4, f2, f4 ; (8)
sd 0(r12), f4 ; (9)
addi r8, r8, #8 ; (10)
addi r12, r12, #8 ; (11)
sub r20, r4, r8 ; (12)
bnez r20 ,loop ; (13)
ld f2, 0(r8) ; (5)

Otra posible mejora consiste en poner una instrucción de la iteración que siempre tiene que
ejecutarse, pero no afecta a la condición del salto. En el programa anterior se podría situar detrás
de la instrucción de salto, una de las instrucciones que actualizan los registros que sirven de
puntero para ir accediendo a los componentes del vector: instrucción (11). En ese caso, el código
sería:

start: ld f0, a ; (1)


add r8, r0, rx ; (2)
addi r4, r8, #512 ; (3)
add r12, r0, ry ; (4)
loop: ld f2, 0(r8) ; (5)
multd f2, f0, f2 ; (6)
ld f4, 0(r12) ; (7)
addd f4, f2, f4 ; (8)
sd 0(r12), f4 ; (9)
addi r8, r8, #8 ; (10)
sub r20, r4, r8 ; (12)
bnez r20, loop ; (13)
addi r12, r12, #8 ; (11)

En cualquier caso, hay que asegurarse que, al cambiar de sitio una instrucción, no se empeora el
efecto de posibles dependencias que no afectaban porque la instrucción retardaba la captación de
una instrucción con respecto a otras. En el código anterior, la instrucción (11) retardaba la
ejecución de (12) respecto de (10). No obstante, si se considera que hay caminos de bypass, esta
dependencia de tipos RAW entre (10) y (12) no tiene efecto.

5. Considere el procesador segmentado que simula WINDLX y considere que implementa


un salto retardado en el que la instrucción situada a continuación de la instrucción de
salto condicional no se anula nunca. Escriba el programa DLX que permite ejecutar:
for (i = 0 ; i < n ; i++)
d[i] = a[i] + b[i] + c[i];

a) Realice las transformaciones necesarias para obtener un código eficiente en el


que se aproveche la instrucción ubicada tras la instrucción de salto condicional.

b) ¿Cómo sería el código si el procesador implementara la opción de anular si no se


salta?

c) ¿Y si la opción es anular siempre?

Solución
Para el caso en que el procesador no anule nunca la instrucción situada a continuación del salto, el
código del programa sería el siguiente:

add r1, r0, r0 ; r1 = 0 (desplazamiento del dato actual en los


vectores)
lw r2, n ; r2 = n (número de elementos en los vectores)
inicio: lw r3, a(r1) ; r3 = a[i]
lw r4, b(r1) ; r4 = b[i]
lw r5, c(r1) ; r5 = c[i]
add r6, r3, r4 ; r6 = a[i] + b[i]
add r6, r5, r6 ; r6 = r6 + c[i]
sw d(r1), r6 ; d[i] = r6
subi r2, r2, #1 ;n=n–1
bnez r2, inicio ; saltamos si n != 0
addi r1, r1, #4 ; r1 apunta a a[i+1]

Como el código utiliza la ALU para hacer todas las operaciones aritméticas, la latencia de todas
las operaciones es de un ciclo, con lo que suponiendo que existen caminos de bypass, no existirían
atascos en el cauce. Para aprovechar el salto retardado se ha retrasado la instrucción que
incrementa r1, que se ejecutará tanto si se salta como si no.

Para el caso de que el procesador anule la instrucción a continuación del salto si no se salta (se
supone que siempre se va a saltar), el código anterior funcionaría sin problemas, ya que sólo se
anularía el incremento de r1 en la última iteración, cuando ya no hace falta volver a usarlo. Por
último, si el procesador anulara siempre la instrucción que está situada a continuación del salto,
habría que subir el incremento de r1 antes del salto y colocar tras el salto una instrucción nop, para
que su anulación no nos cause ningún problema.

6. Considere un procesador con arquitectura DLX que implementa un cauce segmentado en


cinco etapas (IF, ID, EX, MEM, WB). Para los números en coma flotante se añaden
además un sumador con una latencia de 2 ciclos y un multiplicador con una latencia de 3
ciclos. Existen caminos de bypass desde la salida de las unidades de ejecución y desde la
etapa de memoria a las entradas de la ALU. Las instrucciones de salto se procesan en la
etapa de decodificación, por lo que la nueva dirección a la que se ha de acceder si se
produce el salto estará disponible al final de dicha etapa. Se implementa salto retardado
en el que no se anula nunca la siguiente instrucción que se haya podido introducir en el
cauce tras un salto (el salto se resuelve en la etapa de decodificación). Considerando el
código siguiente:

add r1, r0, r0 ; Inicializamos el índice i a 0


subd f0, f0, f0 ; Inicializamos el acumulador a 0
bucle: ld f2, X(r1) ; Cargamos X[i]
multd f6, f2, f4 ; Multiplicamos X[i] por a (en f4)
addd f0, f0, f6 ; Acumulamos el resultado
addi r1, r1, #8 ; Incrementamos el contador del índice
subi r3, r3, #8 ; Comprobamos si hemos acabado
bnez r3, bucle ; Saltamos si quedan elementos
sd R, f0 ; Guardamos el resultado

a) ¿Se ejecutaría el programa correctamente tal y como está? ¿Qué cambios haría
para aprovechar el salto retardado?

b) ¿Cuál es la productividad del cauce en función del número de iteraciones N?

Solución
El programa se ejecuta correctamente, ya que en cada iteración se ejecutará la instrucción de
almacenamiento que hay detrás del salto y el efecto que tendrá será simplemente el de almacenar
la suma parcial acumulada en f0. Sin embargo, esta operación, aunque no provoca que el resultado
sea incorrecto, sí que repercute en el tiempo de ejecución del programa, ya que si el vector X tiene
N elementos, se realizarán N – 1 almacenamientos que consumen un tiempo de ejecución que
podría usarse para algo más provechoso. Por ejemplo, una posible optimización podría ser la
siguiente:

add r1, r0, r0 ; Inicializamos el índice i a 0


subd f0, f0, f0 ; Inicializamos el acumulador a 0
bucle: ld f2, X(r1) ; Cargamos X(i)
subi r3, r3, #8 ; Comprobamos si hemos acabado
multd f6, f2, f4 ; Multiplicamos X(i) por a (en f4)
addd f0, f0, f6 ; Acumulamos el resultado
bnez r3, bucle ; Saltamos si quedan elementos
addi r1, r1, #8 ; Incrementamos el contador del índice
sd R, f0 ; Guardamos el resultado

En este caso, la instrucción de después del salto retardado se utiliza para incrementar el puntero al
siguiente elemento de X, instrucción que hay que realizar de todas formas, con lo que el cuerpo del
bucle pasa a tener una instrucción menos que antes. Además, la instrucción de resta se ha colocado
detrás la instrucción de carga para ocultar su retardo carga-uso.

Para obtener la productividad del cauce primero tenemos que calcular cuánto tiempo tarda en
ejecutarse el programa para un vector de N elementos. Para ello es necesario realizar una traza de
su ejecución, tal y como muestra la Figura 9. En esta traza podemos observar que se producen dos
atascos en el cauce, el primero de ellos en el ciclo 6 debido a un riesgo estructural entre las
instrucciones (2) y (3) en la etapa MEM, y el segundo debido a un riesgo RAW entre las
instrucciones (5) y (6). También podemos observar que se tarda dos ciclos en comenzar la primera
iteración del bucle, que desde que se capta la primera instrucción del bucle transcurren 9 ciclos
hasta que se resuelve el salto, por lo que podemos concluir que cada iteración del bucle tarda 9
ciclos, y que tras la última iteración es necesario terminar de ejecutar las dos últimas instrucciones,
lo que tarda otros cinco ciclos. Por tanto, el tiempo de ejecución del programa es de:

T  N   2  9N  5  9N  7

INSTRUCCIÓN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
M
W
add r1, r0, r0 IF ID EX E
B
M
M
W
subd f0, f0, f0 IF ID EX EX E
B
M
ld f2, X(r1) IF ID EX Sta M W
ll E B
M
M
Sta W
subi r3, r3, #8 IF ID
ll
EX E
B
M
M
Sta W
multd f6, f2, f4 IF
ll
ID EX EX EX E
B
M
M
Sta W
addd f0, f0, f6 ll
IF ID Stall EX EX E
B
M
bnez r3, bucle IF Stall ID
M
W
addi r1, r1, #8 Stall IF ID EX E
B
M
M
W
sd R, f0 IF ID EX E
B
M

Figura 9. Traza de la ejecución del código del problema 6.

Sabiendo el tiempo de ejecución, la productividad del cauce se puede expresar en términos de


resultados por ciclo:

Nº instrucciones 6N  3
WN   instr./ci clo
Tiempo 9N  8

o de instrucciones por ciclo:

Nº resultados N
WN   resultados/ciclo
Tiempo 9N  8

7. Suponga que en el siguiente programa N = 108, a y c son dos vectores de números en


coma flotante, y b es un escalar en coma flotante.

for (i = 0 ; i < N ; i++)
a[i + 2] = (a[i + 1] + a[i]) * b;

c[0] = a[N + 1];

a) Si el programa anterior se implementa en un procesador segmentado con salto


retardado y un hueco para instrucciones tras la instrucción de salto, indique si hay
algún problema en la ejecución del código anterior si el salto: (1) se anula
siempre; (2) no se anula nunca; (3) se anula si se salta; (4) se anula si no se salta.

b) ¿Qué cambios haría en el código en el caso en que hay problemas y para que, si
es posible, se mejoren las prestaciones?

Solución
Una posible codificación del bucle anterior podría ser:

ld f1, b ; cargar en f1 el valor de b


add r1, r0, a ; apuntar r1 donde empieza a[ ]
ld r2, N ; cargar en r2 el número de iteraciones
loop: ld f2, 0(r1) ; cargar f2 con a[i]
ld f3, 4(r1) ; cargar f3 con a[i + 1]
addf f4, f3, f2 ; f4=a[i + 1]+a[i]
mulf f4, f4, f1 ; f4=f4*b
sf 8(r1), f4 ; almacenar f4 en a[i + 2]
add r1, r1, #4 ; apuntar a a[i + 1]
sub r2, r2, #1 ; queda una iteración menos
bnez r2, loop
add r5, r0, c ; apuntar r5 donde empieza c[ ]
sf 0(r5), f4 ; almacenar f4 (a[N + 1]) en c[0]

Dependiendo de la implementación del salto retardado, puede que sea necesario cambiar el código
del programa para asegurar su correcto funcionamiento:

1. En el caso en el que la instrucción que entra en el cauce tras el salto se anule siempre,
sería necesario insertar una instrucción nop entre el salto y la suma, ya que en la última
iteración, en la que no se debe saltar, se anularía la siguiente instrucción al salto y la
operación de almacenamiento final no accedería a la dirección correcta.

2. Si la instrucción siguiente al salto no se anula nunca, el programa podría fallar si dicha


instrucción afecta a los registros que se usan en el bucle. En este ejemplo concreto, como
dicha instrucción afecta a r5, que no se usa en el bucle, el programa funcionaría
correctamente, aunque ejecutaría la instrucción siguiente al salto en todas las iteraciones,
desperdiciando un tiempo de computación que podría usarse en algún cálculo útil.

3. Si la siguiente instrucción al salto se anula sólo si se toma el salto, el programa se puede


dejar tal y como está, ya que sólo se ejecutará cuando se termine el bucle.

4. Por último, si la siguiente instrucción al salto se anula sólo si no se salta, ocurre igual que
en el caso 2, el programa podría fallar si dicha instrucción afecta a los registros que se
usan en el bucle. Pero en este ejemplo concreto el programa funcionaría correctamente si
el bucle itera más de una vez, ya que se fijaría en r5 el valor del puntero donde empieza la
matriz c en todas las iteraciones menos en la última, por lo que en el almacenamiento se
accedería a la dirección correcta. En este caso, también sería conveniente modificar el
programa para ejecutar la instrucción siguiente al salta una sola vez tras la última
iteración y aprovechar el slot del salto para realizar algún cálculo útil.

La Tabla 2 muestran las modificaciones que serían necesarias para optimizar el programa en cada
caso. Cualquiera de las soluciones de los casos 2 y 4 son intercambiables para ambos casos.

SE ANULA SIEMPRE NO SE ANULA NUNCA SE ANULA SI SE SALTA SE ANULA SI NO SE SALTA


ld f1, b ld f1, b ld f1, b ld f1, b
add r1, r0, a add r1, r0, a add r1, r0, a add r1, r0, a
ld r2, N ld r2, N ld r2, N ld r2, N
loop: ld f2, 0(r1) loop: ld f2, 0(r1) loop: ld f2, 0(r1) ld f2, 0(r1)
ld f3, 4(r1) ld f3, 4(r1) ld f3, 4(r1) loop: ld f3, 4(r1)
addf f4, f3, f2 addf f4, f3, f2 addf f4, f3, f2 addf f4, f3, f2
mulf f4, f4, f1 mulf f4, f4, f1 mulf f4, f4, f1 mulf f4, f4, f1
sf 8(r1), f4 sf 8(r1), f4 sf 8(r1), f4 sf 8(r1), f4
add r1, r1, #4 sub r2, r2, #1 add r1, r1, #4 add r1, r1, #4
sub r2, r2, #1 bnez r2, loop sub r2, r2, #1 sub r2, r2, #1
bnez r2, loop add r1, r1, #4 bnez r2, loop bnez r2, loop
nop add r5, r0, c add r5, r0, c ld f2, 0(r1)
add r5, r0, c sf 0(r5), f4 sf 0(r5), f4 add r5, r0, c
sf 0(r5), f4 sf 0(r5), f4

Tabla 2. Diferentes optimizaciones del programa del problema 7.

8. Una función F se va a implementar en un cauce con 5 etapas S1, S2, S3, S4, S5 de forma
que visita las etapas según la secuencia: S1 S5 S1 S4 S3 S2 S1 S4. Obtenga la ganancia
en velocidad con respecto a la ejecución sin cauce para 200 operaciones, teniendo en
cuenta que sin cauce la función requiere un tiempo de 16 ns y que las etapas del cauce
suponen unos tiempos de ejecución de 4 ns para S1, S4 y S5, 5 ns para S2, y 3 ns para S3
(incluyendo los retardos de los registros de acoplo). ¿Cuál es la productividad en función
del número de operaciones? Determine los valores de la productividad máxima y la
ganancia máxima.
Solución
Con ayuda de la Tabla 3, que muestra la tabla de reservas del cauce, podemos obtener sus latencias
prohibidas F = {2, 4, 6}, y su vector de colisiones C = (101010), a partir del cual se puede dibujar
su diagrama de estados, mostrado en la Figura 10. Con ayuda de este diagrama se calcula su
mínima latencia mínima como:

53
MLM   4 ciclos
2

ETAPA 1 2 3 4 5 6 7 8
S1 X X X
S2 X
S3 X
S4 X X
S5 X

Tabla 3. Tabla de reservas del cauce del problema 8.

7+
7+
101010
7+
3
7+ 5 1

5
101111 101011 111111

3 5

Figura 10. Diagrama de estados del cauce del problema 8.

Teniendo en cuenta que el tiempo de los registros de acoplo está incluido en el de las etapas,
fijaremos el tiempo de reloj del cauce a:

t  max t 1 , t 2 , t 3 , t 4 , t 5   d  max 3,4,5  5 ns

Una vez obtenidos MLM y t, podemos calcular el tiempo que tardarían en procesarse n
operaciones en el cauce segmentado como:

Tseg  n   k   n  1  MLM  t   8   n  1  4   5  20 n  1 ns

En cuanto al circuito original, el tiempo de operación para n operaciones sería:

T orig  n  n  T  16n ns

Por tanto, la ganancia en velocidad S(n) del cauce será:

Torig  n 16n 4n
S  n   
Tseg  n 20 n  1 5 n  1

La ganancia obtenida para 200 operaciones se obtiene simplemente sustituyendo n = 200 en la


expresión anterior:
4  200
S  200   0.799
5 200  1

La productividad en función del número de operaciones se define como:

n n n  109
W  n   operaciones/ns  operaciones/s
Tseg  n 20 n  1 20 n  1

Por último, la ganancia y la productividad máximas del cauce se obtienen aplicando el límite
cuando n → ∞ a la ganancia y a la productividad respectivamente:

4n 4
S max  lim S  n  lim 
n 5 n  1 5
n

n  109
W max  limW  n  lim  50  106 operaciones/s
n n 20 n  1

9. Se han encontrado dos posibles alternativas para la ejecución de una función F en un


cauce con 4 etapas S1, S2, S3, S4. La alternativa 1 visita las etapas según la secuencia
S1 S3 S1 S3 S2 S4 S4, y la alternativa 2 en el orden S1 S2 S3 S2 S3 S4 S2.

a) ¿Cuál de las dos alternativas permite ejecutar un número mayor de funciones por
unidad de tiempo? Demuestre razonadamente su respuesta.

b) Obtenga además la ganancia en velocidad que ofrecen cada una de las


alternativas con respecto a su ejecución sin cauce para 1000 operaciones,
teniendo en cuenta que sin cauce la función requiere un tiempo de 15 ns; que las
etapas del cauce suponen unos tiempos de ejecución de: 4ns para S1, 4 ns para
S2, 3 ns S3 y 4 ns para S4; y que los registros introducen retardos de 0.1 ns.

Solución
El primer paso para responder al primer apartado es calcular la tabla de reservas para cada una de
las dos alternativas (Tabla 4 y Tabla 5). A partir de estas tablas se pueden obtener las latencias
prohibidas y los vectores de colisiones de los dos cauces:

 F1 = {1, 2}, C1 = (11)

 F2 = {2, 3, 5}, C2 = (10110)

ETAPA 1 2 3 4 5 6 7
S1 X X
S2 X
S3 X X
S4 X X

Tabla 4. Tabla de reservas para la primera alternativa del cauce del problema 9.

ETAPA 1 2 3 4 5 6 7
S1 X
S2 X X X
S3 X X
S4 X
Tabla 5. Tabla de reservas para la segunda alternativa del cauce del problema 9.

Para determinar qué alternativa tiene la máxima productividad, realizamos un diagrama de estados
para cada cauce a partir de su vector de colisiones (Figura 11 y Figura 12). Con ayuda de estos
diagramas podemos encontrar los ciclos de latencia mínima de cada cauce:

MLM1  3
1 6
MLM2   3.5
2

11

Figura 11. Diagrama de estados para la primera alternativa del cauce del problema 9.

1011
1 4
0

6 6
11111 10111

Figura 12. Diagrama de estados para la primera alternativa del cauce del problema 9.

Una vez calculadas las MLM de los dos cauces, sus tiempos de ejecución se calculan mediante:

Tseg1  n   k 1   n  1  MLM1   t   7   n  1  3  t
Tseg2  n   k 2   n  1  MLM2   t   7   n  1  3.5  t

sus productividades:

n n
W 1  n   operaciones/s
Tseg1  n  7   n  1  3  t
n n
W 2  n   operaciones/s
Tseg2  n  7   n  1  3.5  t

y sus productividades máximas:

n 1 1
W max1  lim W 1  n  lim   operaciones/s
n n  k   n  1  MLM   t MLM1  t 3  t
1 1
n 1 1
W max2  lim W 2  n  lim   operaciones/s
n n  k   n  1  MLM   t MLM  t 3.5 t
2 2 2

De estas expresiones se puede concluir que la productividad de la primera alternativa es mayor que
la de la segunda.

Para responder a la segunda cuestión, primero debemos calcular el tiempo de ejecución secuencial
para n operaciones:
T orig  n  n  15  15n ns

y el tiempo de ciclo de los circuitos segmentados:

t  max t 1 , t 2 , t 3 , t 4   d  max 4,4,3,4  0.1  4.1 ns

Una vez calculados estos dos tiempos, la ganancia en velocidad de cada alternativa se define
como:

T orig  n 15n 15n


S 1  n   
Tseg1  n  7   n  1  3  4.1 12.3n  16.4
T orig  n 15n 15n
S 2  n   
Tseg2  n  7   n  1  3.5  4.1 14.35 n  1

Así que para obtener la ganancia obtenida al procesar 1000 operaciones, sólo hay que sustituir
n = 1000 en las ecuaciones anteriores:

T orig  1000
S 1  1000   1.22
T seg1  1000
T orig  1000
S 2  1000   1.04
T seg2  1000

10. La Tabla 6 muestra la tabla de reservas de un cauce que puede ejecutar dos funciones, A y
B. Determine las latencias prohibidas y construya el diagrama de estados para dicho
cauce.

ETAPA 1 2 3 4 5
S1 A B A B
S2 A B A
S3 B A, B

Tabla 6. Tabla de reservas para el problema 10.

Solución
Los conjuntos de latencias prohibidas y los vectores de colisiones cruzadas para las secuencias de
instrucciones AA, BA, AB, y BB son los siguientes:

 FAA = {3}: Representa la latencia prohibida que aparece al introducir una


instrucción A, para las instrucciones de tipo A que se quieran introducir después.

 FBA = {1, 2}: Representan las latencias prohibidas que aparecen al introducir una


instrucción A, para las instrucciones de tipo B que se quieran introducir después.

 FAB = {1, 2, 4}: Representan las latencias prohibidas que aparecen al introducir


una instrucción B, para las instrucciones de tipo A que se quieran introducir
después.

 FBB = {2, 3}: Representan las latencias prohibidas que aparecen al introducir una


instrucción B, para las instrucciones de tipo B que se quieran introducir después.
Puesto que el valor mayor de latencia prohibida incluida en los conjuntos anteriores es 4, los
vectores de colisiones tendrán 4 componentes y serán:

 CAA = (0100), CBA = (0011)

 CAB = (1011), CBB = (0110)

Las matrices de colisiones cruzadas son:

 C AA   0100
M A      
 C BA   0011
 C AA   1011
M B      
 C BA   0110

La Figura 13 muestra el diagrama de estados obtenido a partir de ellas. En dicho diagrama, desde
cualquier estado se pasa al estado inicial indicado por MA (MB) cuando se introduce una instrucción
A (B) en el cauce tras esperar 5 o más ciclos. Ese es el significado de la flecha que llega a MA (MB)
marcada con A5+ (B5+).

B3, B4

A5+ B5+
A4 A3 B1
A4

A2 B3, B4 B4
010 010 101 111
0 1 1 1

001 A2
001 011 011
1 A4 1 B3, B4 0 1
B4
011
0

A1 001 A1
1

011 B3, B4
1

A4 001
1
Figura 13. Diagrama de estados para el cauce del problema 10.

11. Las cuatro etapas de un cauce multifuncional, S1, S2, S3 y S4, se utilizan por las
instrucciones del tipo A de la forma S1 S2 S1 S3 S1 S4 S1 y por las instrucciones del tipo
B de la forma S1 S4 S1 S3 S1 S2 S1. Escriba las matrices de colisiones cruzadas y
determine cuál es la productividad máxima del cauce para una secuencia de instrucciones
del tipo A (es decir A, A, A,…) teniendo en cuenta que la frecuencia de reloj es de 500
MHz.
Solución
El primer paso para calcular las matrices de colisiones cruzadas es obtener la tabla de reservas,
mostrada en la Tabla 7. Con esta tabla es fácil obtener las latencias prohibidas:

 FAA = {2, 4, 6}, FBA = {2, 4, 6}

 FAB = {2, 4, 6}, FBB = {2, 4, 6}

y los vectores de colisiones del cauce:

 CAA = (101010), CBA = (101010)

 CAB = (101010), CBB = (101010)

Con lo que las matrices de colisiones del cauce quedan como:

 C AA   101010
M A      
 C BA   101010
 C AA   101010
M B      
 C BA   101010 

ETAPA 1 2 3 4 5 6 7
S1 A, B A, B A, B A, B
S2 A B
S3 A, B
S4 B A

Tabla 7. Tabla de reservas para el problema 11.

Para calcular la productividad máxima para una secuencia del tipo A, sólo debemos tener en cuenta
el vector de colisiones CAA = (101010), que nos genera el diagrama de estados que muestra la:

1 5
101010
5
3 5
7
111111 101111 101011

Figura 14. Diagrama de estados para el cauce del problema 11.

La mínima latencia mínima del diagrama se puede obtener como:

1 7
MLM   4 ciclos
2

Con lo que para una secuencia de n operaciones la productividad del cauce es de:

n n F n  500 106 5n
W  n      108 operaciones/s
T  n  k   n  1  MLM  7   n  1  4  4n  3
y la productividad máxima:

5n
W max  lim W (n )  lim  108  125 106 operaciones/s
n n 4n  3

12. La Tabla 8 muestra la tabla de reservas de un cauce multifuncional. ¿Cuál es el tiempo


mínimo (en ciclos) que tardaría en ejecutarse la secuencia de seis instrucciones
A B C B A C si se utiliza una estrategia de tipo avaricioso?

ETAPA 1 2 3 4
S1 A, C B, C
S2 A, B, C A A
S3 B C

Tabla 8. Tabla de reservas para el problema 12.

Solución
A partir de la tabla de reservas del enunciado se pueden obtener las latencias prohibidas del cauce:

 FAA = {1, 2}, FBA = {1, 2}, FCA = {1, 2}

 FAB = {2}, FBB = {}, FCB = {2}

 FAC = {2}, FBC = {2}, FCC = {2}

y las matrices de colisiones para las tres operaciones:

 11  10   10 
     
MA   11, M B   00 , MC   10 
 11  10   10 
     

Siguiendo una planificación avariciosa del cauce, se pasarían por los estados que muestra la Figura
15, por lo que el tiempo total de ejecución de la secuencia sería de 16 ciclos, como si indica en el
diagrama de tiempos de la Figura 16.

1 1
A3
1 1

C3 1 0 B1
1 1

1 B3 1
1 1 1
1 C1
1
0 0 1

1 0 1
0 0 0
Figura 15. Diagrama de estados para el cauce del problema 12.
1 1 1
1 2 3
0 4 5 6 7 8
0 9 10 11 12 13
114 15 16
A
B
C
B
A
C

Figura 16. Traza de la ejecución de la secuencia de instrucciones del problema 12.

13. La Tabla 9 muestra la tabla de reservas de un cauce multifuncional ¿Cuál es el tiempo


mínimo que tardaría en ejecutarse la secuencia de instrucciones A C C A B B si se utiliza
una estrategia de tipo avaricioso?

ETAPA 1 2 3 4
S1 A, C B
S2 A, B, C B A
S3 B A, C

Tabla 9. Tabla de reservas para el problema 13.

Solución
Las latencias prohibidas del cauce son:

 FAA = {2}, FBA = {1, 2}, FCA = {2}

 FAB = {1, 2}, FBB = {1}, FCB = {1, 2}

 FAC = {}, FBC = {2}, FCC = {}

Por lo tanto, las matrices son:

 10  11  00 
     
M A   11, M B   01, M C   10 
 10  11  00 
     

A partir de estas matrices se puede dibujar el diagrama de estados que muestra la Figura 17 y la
traza que muestra la Figura 18, que indica que a secuencia de operaciones tarda 12 ciclos en
ejecutarse.

1 0
A1
0 0
B3
1 1
B2 1 0

1 C1 C2 0
1 0
1 0 1 0

0 1
1 1
Figura 17. Diagrama de estados para el cauce del problema 13.
1 0
1 2
1 3 4 5 6 7
18 9 10 11 12
A
C
C
A
B
B

Figura 18. Traza de la ejecución de la secuencia de instrucciones del problema 13.

14. Se tiene un cauce multifuncional con 4 etapas, S1, S2, S3, S4, que puede ejecutar dos
tipos de operaciones, X, e Y. Las operaciones del tipo X recorren el cauce según la
secuencia S2 S4 S3 S1 S1 S2, mientras que las de tipo Y lo hacen en el orden
S1 S2 S1 S2 S3 S4. Si el bucle interno de un programa realiza intensivamente la
secuencia de operaciones X Y Y X X Y Y Y, ¿cuánto tiempo tardaría en ejecutarse el
cuerpo del bucle si la frecuencia de reloj es de 1 GHz?, ¿Cuál es la productividad máxima
del cauce para esa secuencia?

Solución
A partir de la tabla re reservas del cauce (Tabla 10) podemos determinar las matrices de colisiones
del cauce:

1 0 0 0 1 0 1 1 1 1
M X   , M Y   
0 1 1 1 1 0 0 0 1 0 

Como en el enunciado nos piden que calculemos cuánto tiempo tarda en ejecutarse la secuencia de
operaciones X Y Y X X Y Y Y, no tenemos que dibujar el diagrama de estados completo, sino sólo
la parte que se utiliza para determinar las latencias de esta secuencia, tal y como muestra la Figura
19.

ETAPA 1 2 3 4 5 6
S1 Y Y X X
S2 X Y Y X
S3 X Y
S4 X Y

Tabla 10. Tabla de reservas para el problema 14.

X (1)
X5 (4)

1000 0111
1 X5 5+11ra el 1
diagrama de
estados del cauce, Y3 (8) 0001
0111
X2 (5) 1 del que se puede 1
Y5 (2)
obtener su Y1 (3,7)
MLM = hla
1010 ejecuci 0 1 1 1
1 1
(1 -
0111 Y5 (6)
Sig. Iter.)0 0 0 1
1 0

Figura 19. Diagrama de estados para el cauce del problema 14.


Una vez dibujado el diagrama de estados, ya conocemos el instante en el que se inicia la ejecución
de cada operación en el cauce, por lo que podemos dibujar la traza de la ejecución, mostrada en la
Figura 20. Con ayuda de esta gráfica podemos concluir que la secuencia de operaciones tarda 28
ciclos, que con un reloj de 1 GHz son 28 ns.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
X
Y
Y
X
X
Y
Y
Y
Siguiente iteración del X
bucle

Figura 20. Traza de la ejecución de la secuencia de operaciones del problema 14.

Para calcular la productividad máxima para esta secuencia tenemos que tener en cuenta que la
siguiente iteración no puede comenzar hasta el ciclo 28, ya que tenemos que esperar 5 ciclos
después de la última Y para introducir la X de la siguiente secuencia, como indica el diagrama de
estados, así que podremos empezar una nueva secuencia cada 27 ciclos, por tanto, el tiempo para
calcular n secuencias será:

TLI   n  1  MLM 28   n  1  27
T  n    27n  1 ns
F 109

Y la productividad máxima:

n n  109
W max  limW  n  lim  lim  37.04  106 iteraciones/s
n n T  n n   28   n  1  27

15. Un procesador segmentado tiene 5 etapas S1, S2, S3, S4, S5 y todas tardan un ciclo
excepto la etapa S3 (etapa de ejecución) que, según las instrucciones puede tardar 2 o 3
ciclos. ¿Cuál es la velocidad pico de ese procesador si funciona a 500 MHz? ¿Qué mejora
podría implementar para mejorar esa velocidad pico? ¿Qué valor alcanzaría entonces?

Solución
Teniendo en cuenta que una etapa dura dos ciclos para un tipo de instrucciones y tres ciclos para
otro tipo, en el mejor de los casos, si todas las instrucciones del programa son de aquellas en las
que la etapa S3 duran dos ciclos, se puede terminar una instrucción cada dos ciclos. Por tanto, la
velocidad pico se puede estima a partir de:

F 500 106
R pico    250 106 instrucciones/s
CPI 2

Este apartado se podría resolver de forma más rigurosa a partir de la tabla de reservas del cauce
(Tabla 11), de la que se pueden obtener sus latencias prohibidas:

 FAA = {1}, FBA = {1}

 FAB = {1, 2}, FBB = {1, 2}


Una vez determinadas sus latencias prohibidas, se puede obtener su diagrama de estados (Figura
21), en el que se puede comprobar fácilmente que cualquier ciclo que se construya tiene una
latencia media que estará entre 2 (como mínimo) y 3 (como máximo).

ETAPA 1 2 3 4 5 6 7
S1 A, B
S2 A, B
S3 A, B A, B B
S4 A B
S5 A B

Tabla 11. Tabla de reservas para cauce del problema 15.

A2+ B2 B3+

0 1
1 1

0 A3 1
1 1

Figura 21. Diagrama de estados para el cauce del problema 15.

La velocidad pico se corresponderá con la situación en que la MLM (mínima latencia media) sea
igual a 2. Con esta MLM la productividad máxima del cauce será de:

n n F F 500 106
W max  limW  n  lim  lim    250 106 instrucciones/s
n  n  T  n  n   k   n  1  MLM MLM 2

Para mejorar la velocidad del procesador, una posible mejora consiste en añadir otras dos etapas
S3 que puedan funcionar simultáneamente, tal y como muestra la Figura 22. Además de añadir
estas dos etapas S3 adicionales, habría que mantener las instrucciones de tipo A en la etapa S3
durante tres ciclos en lugar de dos ciclos.

S3

S1 S2 S3 S4 S5

S3

Figura 22. Primera alternativa para la optimización del cauce del problema 15.

Otra posibilidad consiste en rediseñar la etapa S3 de forma que conste de tres etapas
independientes de un ciclo cada una de ella, tal y como muestra la Figura 23. En este caso,
también debe ocurrir que las instrucciones de tipo A pasen por las tres etapas nuevas.

S1 S2 S31 S32 S33 S4 S5


Figura 23. Segunda alternativa para la optimización del cauce del problema 15.

Con cualquiera de estas modificaciones, el tiempo de latencia inicial de cualquier instrucción sería
de 7 ciclos, y cada ciclo terminaría de ejecutarse una instrucción. De esta forma, la velocidad pico
sería:

F 500  106
R pico    500  106 instrucciones/s
CPI 1

16. La Tabla 12 muestra la tabla de reservas de un sumador segmentado con cuatro etapas S1,
S2, S3, S4. Indique cuál es la productividad máxima de este cauce si se utiliza un reloj
con una frecuencia de 2 GHz. ¿A partir de qué número de sumas segmentadas
consecutivas se alcanza el 90% de la productividad máxima? ¿Qué cambios haría en la
etapa S3 para mejorar el rendimiento del cauce?

ETAPA 1 2 3 4 5
S1 X X
S2 X
S3 X X
S4 X

Tabla 12. Tabla de reservas para cauce del problema 16.

Solución
El conjunto de latencias prohibidas del cauce es F = {1, 4}, por lo tanto, su vector de colisiones es
C = (1001). La Figura 24 muestra el diagrama de estados del cauce, del que se puede obtener su
MLM = 2.5.

La productividad del cauce es:

n n F n  2  109 8n
W  n      108 operaciones/s
T  n  TLI   n  1  MLM  5   n  1  2.5 n  1

Por tanto, la productividad máxima es:

8n
W max  lim W  n  lim  108  8  108 operaciones/s
n  n n  1

3,

100
5+
1
3, 5+ 2

101
1
Figura 24. Diagrama de estados para el cauce del problema 11.

Para determinar el valor de n con el que se alcanza el 90% de la productividad máxima se despeja
n de la igualdad:

0.9  W max  0.9  8  108   8n
n1
 108

obteniéndose n = 9.

Para mejorar las prestaciones del cauce, se podría segmentar la etapa S3 en dos nuevas etapas de la
mitad de duración, tal y como muestra la Tabla 13. Así, en lugar de 4 se tendrían 5 etapas y la
única latencia prohibida sería F = {4}. Si se hace el diagrama de estados partiendo de C = (1000)
se observa que la mínima latencia media que se obtiene en este caso es menor que 2.5.

ETAPA 1 2 3 4 5
S1 X X
S2 X
S31 X
S32 X
S4 X

Tabla 13. Tabla de reservas para cauce optimizado del problema 16.

17. Problema de interrupciones 1

18. Problema de interrupciones 2

19. Problema de interrupciones 3

20. Problema de interrupciones 4

S-ar putea să vă placă și