You are on page 1of 35

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:

Torig 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:

Torig 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 Sk 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
Wk n     (6)
Tseg 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
Wmax  lim Wk 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
add r4, r5, r2 IF ID EX WB
addi r5, r5, #1 IF ID EX WB
sub r3, r3, r1 IF ID EX WB
bnez r3, inicio IF ID EX WB
addi r2, r2, #1 IF ID EX WB
lw r6, 0(r2) IF ID EX WB
add r6, r3, r1 IF ID EX WB

(a) Riesgo de control introducido por el salto.

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

(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 número
7 00001011
de bits que indique el arco
4
10110001 Estado inicial (vector de colisiones)
10111011

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:

Torig 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:

Torig n  450n 3n
S n    
Tseg 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
Tseg 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 
Wmax  lim W 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 10 11 12 13 14 15 16 17 18 19
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 r3, r3, r4 IF ID Stall EX MEM OS
add r5, r0, 0x1ac IF Stall ID EX MEM OS
add r6, r0, 0x1fc Stall IF ID EX MEM OS
sub r5, r5, #4 IF ID EX MEM OS
sub r6, r6, #4 IF ID EX MEM OS
sw (r5), r3 IF ID EX MEM OS
sw (r6), r4 IF ID EX MEM OS

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
Tdesenrollado N   4  28  3  14 N  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
Tdesenrollado2 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
add r1, r0, r0 IF ID EX MEM WB
subd f0, f0, f0 IF ID EX EX MEM WB
ld f2, X(r1) IF ID EX Stall MEM WB
subi r3, r3, #8 IF ID Stall EX MEM WB
multd f6, f2, f4 IF Stall ID EX EX EX MEM WB
addd f0, f0, f6 Stall IF ID Stall EX EX MEM WB
bnez r3, bucle IF Stall ID
addi r1, r1, #8 Stall IF ID EX MEM WB
sd R, f0 IF ID EX MEM WB

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./ciclo
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:

Torig 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
Wmax  lim W 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.

10110
1 4

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   k1  n  1 MLM1  t  7  n  1 3 t


Tseg2 n   k2  n  1 MLM2  t  7  n  1 3.5 t

sus productividades:

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

y sus productividades máximas:

n 1 1
Wmax1  lim W1 n   lim   operaciones/s
n  n  k1  n  1 MLM1  t MLM1  t 3  t
n 1 1
Wmax2  lim W2 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:

Torig 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:
Torig n  15n 15n
S1 n    
Tseg1 n  7  n  1  3  4.1
12.3n  16.4
Torig n  15n 15n
S 2 n    
Tseg 2 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:

Torig 1000
S 1 1000   1.22
Tseg1 1000
Torig 1000
S 2 1000   1.04
Tseg 2 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
0100 0101 1011 1111
0011 0011 0110 0111

A2
A4 B3, B4
B4
0110
0011

A1 A1

0111 B3, B4
0011

A4

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
Wmax  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 
     
M A   11, M B   00 , M C   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.

11 11
A3
11 01
11 11
C3
B1

B3
10 10 11
C1
10 00 10
10 10 11

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 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.

10 00
A1
11 10
B3 10 00

B2
C1 C2
11 01
01 11
11 01

Figura 17. Diagrama de estados para el cauce del problema 13.

1 2 3 4 5 6 7 8 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)

10001 01111
01111 X5 (1 - Sig. Iter.) 00011

Y3 (8)
X2 (5)
Y5 (2)
Y1 (3,7)

10101 01111
01111 00010

Y5 (6)

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 bucle X

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
Wmax  lim W 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+

01 11
01 11

A3

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


Wmax  lim W 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
Wmax  lim W n   lim  108  8  108 operaciones/s
n  n  n 1

3,

1001
5+
3, 5+ 2

1011

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 Wmax  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

You might also like