sábado, 12 de enero de 2013

Práctica 8


Grado en Ingeniería Informática                              
Arquitectura de Computadores – Prácticas Curso 2012-13   

P8 - PRÁCTICAS CON WinDLXV

PRÁCTICA 2
El siguiente programa (pract2v1.s) se encarga de multiplicar todos los números de la lista
almacenada por el número pi, dejando los resultados almacenados en las mismas posiciones.

Considerar inicialmente las siguientes opciones de configuración:
-
Unidad de suma: latencia 2 ciclos
-
Unidad de multiplicación: latencia 5 ciclos
-
Unidad de división: latencia 19 ciclos
-
Unidades funcionales sin segmentar
-
Saltos: predicción de no tomar
-
Adelantamiento de resultados: desactivado
Para empezar debemos establecer la configuración que nos indica el enunciado:
Configuración → Arquitectura (Ctrl+A)

*Para obtener los valores de latencia deseados en el enunciado debemos poner los que aparecen en
la imagen.
En el mismo menú Configuración, desactivamos adelanto de resultados y la opción de saltos la
dejamos tal cual, es decir, predicción de no tomar.
1.-

a) Simular el programa y determinar la ganancia de velocidad que se obtiene si se permiten caminos
de bypass (opción adelantamiento de resultados activada).
Primero ejecutamos las instrucciones con la opción adelantamiento de resultados desactivada,
obtenemos lo siguiente:
A(pract2v1.s)

Ahora activaremos la opción adelantamiento de resultados y obtenemos los siguientes resultados:
B

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/392 = 0,002551102
VelocidadB = PrestacionesB = 1/294 = 0,003401361
PrestacionesB / PrestacionesA = 0,003401361 / 0,00255110 = 1,333
Luego con el adelantamiento de resultados activado, habrá una ganancia de velocidad del 33%.
b) Partiendo de la configuración inicial, considere que se efectúa una mejora en la unidad de
multiplicación, reduciendo su retardo a 3 ciclos. Esta mejora supone un incremento del coste del
15%. Determine si, para el este programa,
compensa realizar dicha mejora desde el punto de vista de la relación coste/prestaciones.
Para este apartado la configuración será la siguiente:
Configuración → Arquitectura (Ctrl+A)

Como ya sabemos del enunciado anterior la configuración inicial produce los siguientes resultados:
A(pract2v1.s)

Con la latencia de multiplicación reducida, obtenemos lo siguiente:
B

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/392 = 0,002551102
VelocidadB = PrestacionesB = 1/344 = 0,002906977
PrestacionesB / PrestacionesA = 0,002906977 / 0,002551102 = 1,13
Viendo los resultados, esta mejora nos aumentará el coste un 15% y a cambio nos dará un
rendimiento 13% mayor, por lo que no es una mejora efectiva en relación coste/prestaciones.
c) Considere que la mejora consiste en reducir el retardo de la unidad de
multiplicación a 2 ciclos, con un incremento del coste del 18%. ¿Compensa la mejora?
Para este apartado la configuración será la siguiente:
Configuración → Arquitectura (Ctrl+A)

Como ya sabemos del enunciado anterior la configuración inicial produce los siguientes resultados:
A(pract2v1.s)

Con la latencia de multiplicación reducida, obtenemos lo siguiente:
B

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/392 = 0,002551102
VelocidadB = PrestacionesB = 1/320 = 0,003125
PrestacionesB / PrestacionesA = 0,003125 / 0,002551102 = 1,224
Viendo los resultados obtenidos, esta mejora nos aumentará el coste un 18% y a cambio nos dará un
rendimiento 22% mayor, por lo que es una mejora efectiva en relación coste/prestaciones.
d) ¿Tendría interés mejorar la latencia de la unidad de suma?
No seria efectiva, ya que si observamos en la ventana estadística un poco mas abajo, observamos las
instrucciones de tipo suma que se llevan a cabo:

A la vista de esto, no tendrá ningún interés mejorar la latencia de suma.
2.-
 Seleccione la opción “salto retardado” y ejecute el programa. ¿Qué ocurre? ¿Cuál es la causa?
¿Cuál sería la solución?
En el salto retardado se reordena el código de forma que se sitúan instrucciones que no dependan
del salto después del mismo para que se vayan ejecutando esas instrucciones (que siempre se van a
ejecutar) y cuando se termine de calcular el salto se toma o no. Pero en este programa hay un
problema. Lo que pasa es que antes de que se decida el salto, como se siguen ejecutando
instrucciones, Se ejecuta la Trap de salida antes de que se tome el salto. Por eso solo hace una
iteración del bucle y finaliza, con lo cual no se ejecuta bien el programa. La solución sería poner
más instrucciones independientes a ejecutarse después del salto, para que no se ejecute el trap antes
de coger el salto.
3.-
 La siguiente figura (pract2v2.s) muestra una versión modificada del código inicial, que se ha
transformado aplicando una técnica que recibe el nombre de desenrollado de bucle. Esta técnica
consiste en realizar los cálculos de varias iteraciones en una única iteración para reducir así el
número de instrucciones de salto que se tienen que ejecutar, con lo que además se evitan riesgos de
control. Determinar la ganancia de velocidad que se obtiene con respecto a la versión inicial del
programa.

Como ya sabemos del enunciado anterior la configuración inicial produce los siguientes resultados:
A(pract2v1.s)

Con la versión mejorada del programa obtenemos los siguientes resultados:
B(pract2v2.s)

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/392 = 0,002551102
VelocidadB = PrestacionesB = 1/302 = 0,003311258
PrestacionesB / PrestacionesA = 0,003311258 / 0,002551102 = 1,29
Luego el nuevo programa ha producido una ganancia de velocidad del 29% respecto al anterior.
4.-
 Para el programa del apartado anterior, pract2v2.s, determinar la ganancia de velocidad que se
obtiene con respecto a un procesador DLX sin segmentar de referencia, que denominaremos
DLXssr.
En el procesador DLXssr cada instrucción va pasando por distintas fases que se suceden según sean
utilizadas por la correspondiente instrucción, de forma que las instrucciones tendrán distinto tiempo
de ejecución según del tipo que sean.
Consideraremos que en DLXssr las fases IF, ID, intEX, MEM y WB tienen un ciclo de reloj cada
una. Las fases faddEX, fmultEX y fdivEX tendrán el mismo número de ciclos que en el caso del
procesador segmentado.
Para estimar el tiempo de ejecución del programa en DLXssr hay que tener en cuenta el tiempo que
tardaría cada una de sus instrucciones. El tiempo de una instrucción se puede determinar a partir del
número de etapas por las que pasa. Así, por ejemplo, una instrucción de carga (ld) pasaría por todas
las etapas (duración 5 ciclos de reloj) mientras que una instrucción de almacenamiento (sd) no
pasaría por WB (duración 4 ciclos de reloj) ya que no tiene que almacenar nada en los registros del
procesador. Las instrucciones aritmético-
lógicas no pasarían por la etapa MEM. Hay que tener en
cuenta que la duración de la etapa EX depende del tipo de instrucción: si la operación es con
enteros, se utilizará intEX y la duración será de un ciclo; si es en coma flotante, se utilizará faddEX,
fmulEX o fdivEX y la duración será la asignada a la correspondiente unidad.
Para las instrucciones de salto condicional considere una duración de 3 ciclos (en la etapa de
ejecución se comprueba la condición y se carga en el PC la nueva dirección, si fuese el caso). Para
el trap considere 2 ciclos (hasta la etapa de decodificación).
En un procesador sin segmentar cada instrucción se ejecuta independientemente, por lo tanto el
tiempo que tarde en ejecutarse cada instrucción dependerá del numero de ciclos que tenga. Por lo
tanto para hacer la comparación entre este procesador y el segmentado, debemos ver el numero de
instrucciones que tenemos de cada tipo en el programa y los ciclos que cada una necesita.
Pues veamos entonces:
Tenemos 24 elementos en el vector. A cada uno de ellos lo sacamos del vector y lo multiplicamos
por PI y lo guardamos en el mismo sitio.
Por cada elemento → ld(cargar en registro) multd(multiplicar) y sd(volver a guardarlo en el mismo
sitio).
Luego 3 instrucciones por elemento 3*24 =72 instrucciones.
Además, se hacen más instrucciones. En cada iteración del bucle se hace una resta y la
comparación del salto. En esta versión del programa en cada iteración se multiplican 4 elementos
del vector,
Luego 24/4 =6 iteraciones, es decir 6(iteraciones)*2(instrucciones) = 12 instrucciones.
Se ejecutan al principio otras dos instrucciones (una carga y una suma). Y al final otra (trap).
Tenemos en total : 72+12+2+trap = 87 instrucciones.
Ahora veamos los ciclos que tardan cada instrucción y multipliquemos por el número de
instrucciones que hubiera de ese tipo. (LW dura 5 ciclos, pero sw y add duran solo 4 ciclos,
mientras que el salto dura 3 ciclos y la trap 2)
LD (load double) → 24 instrucciones * 5 ciclos = 120 ciclos;
MultD → Tienen 4 ciclos pero hay que contar la latencia de mult, que es 5 ciclos, luego son
3(IF,ID, WB) + 5 de ejecutar la operación = 8 ciclos /instrucción * 24 instrucciones = 192 ciclos.
SD (store double) → 24 instrucciones * 4 ciclos = 96 ciclos.
Subi (substract inmediate) → se hacen 6 instrucciones que duran 4 ciclos (son restas de enteros),
luego 6*4=24 ciclos.
Beqz (El salto) 6 instrucciones * 3 ciclos = 18 ciclos.
De las dos instrucciones del principio, la carga es de 5 ciclos, y la suma de 4, luego 9 ciclos.
trap vale 2 ciclos.
Sumando en total salen 120+192+96+24+18+9+2= 461 ciclos.
Pues bien ya tenemos los datos, ahora comparemos entre ambos procesadores:
A(pract2v2.s)

B:    461 Ciclos
      87 Instrucciones

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/302 = 0,003311258
VelocidadB = PrestacionesB = 1/461 = 0,002169197
PrestacionesA / PrestacionesB = 0,003311258 / 0,002169197 = 1,52
Luego a tenor de los resultados obtenidos, el procesador segmentado es un 52% mas rápido.
5.-
 Modificar el programa del apartado 3 (pract2v3.s) de forma que incluya 8 iteraciones en el
bucle. Determinar la ganancia de velocidad que se obtiene con respecto a las versiones anteriores.
La versión modificada quedaría de la siguiente forma:
.data 0
a: .double 3.14159265358979
x: .double 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
.double 17,18,19,20,21,22,23
xtop: .double 24
.text 256
start: ld f2,a
addi r1,r0,xtop
loop: ld f0,0(r1)
multd f4,f0,f2
sd 0(r1),f4
ld f6,-8(r1)
multd f8,f6,f2
sd -8(r1),f8
ld f10,-16(r1)
multd f12,f10,f2
sd -16(r1),f12
ld f14,-24(r1)
multd f16,f14,f2
sd -24(r1),f16
ld f18,0(r1)
multd f20,f18,f2
sd -32(r1),f20
ld f22,-8(r1)
multd f24,f22,f2
sd -40(r1),f24
ld f26,-16(r1)
multd f28,f26,f2
sd -48(r1),f28
ld f30,-24(r1)
multd f6,f30,f2
sd -56(r1),f6
subi r1,r1,64
bnez r1,loop
trap 6
Ahora comparemos los resultados de ambas versiones del programa:
A(pract2v2.s)

B(pract2v3.s)

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/302 = 0,003311258
VelocidadB = PrestacionesB = 1/287 = 0,003484321
PrestacionesB / PrestacionesA = 0,003484321 / 0,003311258 = 1,11
Con esta nueva versión del programa hemos obtenido una ganancia de velocidad del 11%.
6.-
 Partiendo de la versión del apartado 3, reorganizar las instrucciones (pract2v4.s) para reducir el
efecto de las dependencias entre ellas. Simular la versión realizada y determinar la ganancia de
velocidad obtenida.
Pues bien ahora reorganizaremos las instrucciones dentro del bucle, de forma que primero haremos
las 4 instrucciones de tipo ld(cargar en registro), luego las 4 multd(multiplicar) y por ultimo las 4
sd(volver a guardarlo en el mismo sitio).
La nueva versión del programa quedaría de la siguiente forma:
.data 0
a: .double 3.14159265358979
x: .double 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
.double 17,18,19,20,21,22,23
xtop: .double 24
.text 256
start: ld f2,a
addi r1,r0,xtop
loop: ld f0,0(r1)
ld f6,-8(r1)
ld f10,-16(r1)
ld f14,-24(r1)
multd f4,f0,f2
multd f8,f6,f2
multd f12,f10,f2
multd f16,f14,f2
sd 0(r1),f4
sd -8(r1),f8
sd -16(r1),f12
sd -24(r1),f16
subi r1,r1,32
bnez r1,loop
trap 6
Veamos los resultados obtenidos, primero vemos el que ya conocemos pract2v2.s (A) y luego
pract2v4.s(B).
A(pract2v2.s)

B(pract2v4.s)

Comparando estos resultados tenemos que:
Velocidad = Prestaciones = 1/ciclos
VelocidadA = PrestacionesA = 1/302 = 0,003311258
VelocidadB = PrestacionesB = 1/200 = 0,005
PrestacionesB / PrestacionesA = 0,005 / 0,003311258 = 1,51
Con esta nueva versión del programa (pract2v4.s), hemos obtenido una ganancia de velocidad del
51%.

No hay comentarios:

Publicar un comentario