P2: Rendimiento en la Pico - myTeachingURJC/Arq-computadores-01 GitHub Wiki
Sesión 2: Calcular Rendimiento
En esta práctica pondremos en práctica los conceptos estudiados de rendimiento, tiempo de ejecución y CPI.
IMPORTANTE: Para realizar esta práctica, es necesario que hayas leido en casa antes la sesión S3, abrela en otra pestaña para consultar conceptos que no recuerdes.
Solo necesitaremos la RPi y el cable USB, guarda el resto de elementos dentro de la caja.
1. Instrucciones, ciclos y CPI
Como hemos visto en la S3, ejecutar una instrucción puede requerir más de un ciclo de reloj en el procesador. El número de ciclos que se necesitan para ejecutar un programa depende del programa escrito (número de instrucciones, tipo de instrucciones, etc.), y de los ciclos que requiere cada tipo de instrucción (CPI).
Vamos a utilizar un programa de prueba, y calcular todos estos valores. Utilizaremos el siguiente programa, que pasa el string "Calculando el rendimiento en el RISC-V" a mayúsculas:
.data
cad_mod:.space 100
cad: .string "\nCalculando el rendimiento en el RISC-V\n"
.text
#-- Inicializar el contador de caracteres leidos
li s1, 0
bucle:
#----------- Pasar a mayúsculas
#-- obtener puntero
la t3, cad #-- Dirección de la cadena
add t3, t3, s1 #-- Sumamos la pos del caracter
#-- Leer caracter
lb t0, 0(t3)
#-- Condicion de terminacion
beq t0, zero, fin
#-- Si el caracter es menor que 'a' o mayor que 'z'
#-- hay que pasar al siguiente
li t1, 'a'
blt t0,t1,next
li t1, 'z'
bgt t0, t1, next
#-- Hay que pasarlo a mayusculas
addi t0,t0,-32
next:
#-- Guardarlo
la t4, cad_mod
add t4, t4, s1
sb t0, 0(t4)
#-- Pasar al siguiente caracter
addi s1, s1, 1
#-- Repetir
j bucle
fin: #-- Hemos terminado
#-- Imprimir la cadena
la a0, cad_mod
li a7, 4
ecall
#-- Terminar
li a7, 10
ecall
Copia y pega el código en el simulador RARS y comprueba que funciona.
1. Contar instrucciones:
Para contar el número de instrucciones, solo tenemos que fijarnos en el código, y contar las instrucciones básicas que tiene. Cuidado, recuerda que algunas pseudo instrucciones, equivalen a más de una instrucción.
Pregunta 1: Indica el número de instrucciones básicas que tiene el bucle (desde la etiqueta bucle hasta la etiqueta fin)
Como habrás visto, el número de instrucciones que se ejecutan en cada ciclo del bucle, depende del caracter leído. Además, el bucle se ejecuta varias veces.
Contar a mano todas las instrucciones que tendría un programa puede hacerse demasiado complicado, por ello vamos a utilizar la herramienta específica del RARS que se encuentra en tools -> Instruction Statistics.
Pregunta 2: Indica el número de instrucciones básicas que tiene el programa de prueba completo, sin contar las llamadas al sistema para imprimir el string y finalizar el programa
2. Calcular el número de ciclos
El RARS en un simulador de RISC-V. El número de ciclos dependerá del procesador concreto que utilicemos (cómo esté implementado, si usa segmentación, etc.). Para calcular el número de ciclos, utilizaremos la RPi Pico 2 y lo calcularemos para ese RISC-V en concreto. Recuerda, si utilizásemos otro RISC-V distinto, estos valores cambiarían.
En primer lugar, vamos a probar el código anterior en la RPi. Como no tenemos Sistema Operativo (SO), tenemos que hacer algunas modificaciones. Copia y pega los bloques del código anterior (.data y .text) en la plantilla de la pico, y recuerda dejar la llamada jal init y todos los .eqv, .include y .global.
Como no tenemos SO, las llamadas a ecall no funcionarán. Sustituye la llamada a PRINT_STRING (servicio 4) por la subrutina jal print, pasándole la dirección del string en a0, y la llamada a EXIT (servicio 10) por un bucle infinito.
Ensambla y prueba que se imprime correctamente por la terminal el string en mayúsculas.
Una vez funcione, vamos a contar el número de ciclos que se necesitan para convertir el string a mayúsculas.
Para ello, la RPi tiene un contador interno que se incrementa en cada ciclo de reloj. Para activarlo, tenemos que ejecutar las siguientes instrucciones:
#-- Arrancar contador
li t0, MTIME_CTRL
li t1, 0x3
sw t1, 0(t0)
Esto hará que el contador empiece a funcionar. El contador actualiza el número de ciclos que han pasado en un registro especial, al cual se puede acceder a través de la dirección de memoria MTIME. Por lo tanto, para medir el número de ciclos de un bloque de código, pondremos algo como esto:
#-- Inicio de la medición
li s10, MTIME
lw s11, 0(s10)
#-- BLOQUE
#-- DE CÓDIGO
#-- A MEDIR
#-- Fin de la medición
lw t2, 0(s10)
sub t2, t2, s11
addi t2, t2, -1 #-- Restamos 1 por la instrucción lw
mv a0, t2
jal print_unsigned_int #-- Imprimimos el valor
Hemos guardado en s11 el estado del contador al principio, y si se lo restamos a t2, que es el estado al final del bloque del código, y le restamos 1 por la instrucción lw, obtendremos el número de ciclos que han pasado.
Adapta tu código para contar el número de ciclos del programa de prueba completo, sin contar la llamada a print ni el bucle infinito.
Pregunta 3: Indica el número de ciclos del programa de prueba.
3. Calcular el CPI medio
Con estos datos, podemos obtener el número de ciclos que hace falta para ejecutar una instrucción, es decir el CPI medio
Pregunta 4: Calcula el CPI medio del programa de prueba.
4. Calcular el Tiempo de ejecución
Si sabemos que la frecuencia son 150MHz, cuánto tiempo tarda en ejecutarse el código de prueba.
Pregunta 5: Calcula el tiempo de ejecución en microsegundos.
Ahora, vamos a medir el tiempo con un temporizador interno del procesador
De igual manera, con las siguientes instrucciones puedes obtener el tiempo que ha transcurrido en microsegundos en t2:
#-- Leer temporizador (en us)
li s10, TIMELR
lw s11, 0(s10)
#-- BLOQUE
#-- DE CÓDIGO
#-- A MEDIR
#-- Leer temporizador (en us)
lw t2, 0(s10)
sub t2, t2, s11
mv a0, t2
jal print_unsigned_int #-- Imprimimos el valor
Pregunta 6: Mide el tiempo de ejecución en microsegundos.
Compara ambos tiempos.
Pregunta 7: Cuál es más preciso y por qué?
2. Optimización
Ahora que ya sabemos medir el tiempo que tarda en ejecutarse nuestro código de prueba, vamos mejorarlo.
Para aumentar el rendimiento de un programa, como hemos visto en la teoría, se pueden utilizar varias técnicas:
- Reducir el CPI: Esta implementación del RISC-V en la RPi, no es muy eficiente y requiere muchos ciclos de media para ejecutar cada instrucción, podríamos utilizar otra implementación mejor del RISC-V que requiera menos ciclos por instrucción.
- Aumentar la frecuencia: En ocasiones los procesadores para consumir menos, no funcionan a su frecuencia máxima, se podría aumentar la frecuencia, o utilizar otra implementación de RISC-V que permita frecuencias más altas.
- Mejorar el código: Se puede mejorar el número de ciclos utilizando instrucciones más rápidas (con menor CPI) o reduciendo el número de instrucciones.
En nuestro caso, sólo tenemos la RPi, de la cual no podemos modificar la frecuencia de funcionamiento, por lo que mejoraremos el rendimiento mejorando nuestro código.
Pregunta 8: Detecta qué mejoras se pueden introducir en el código de prueba para reducir el número de ciclos de nuestro programa de prueba.
Calcula el número de ciclos después de las mejoras introducidas, y después el tiempo de ejecución.
Pregunta 9: Calcula el tiempo de ejecución del programa de prueba optimizado.
Ahora, compara ambos tiempos y obtén el rendimiento relativo, o cuánto mejor es el programa optimizado que el original.
Pregunta 10: Calcula el Rendimiento Relativo
Autores
- Miguel Ángel de Miguel
- Juan González-Gómez (Obijuan)
Enlaces
Licencia
