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

Enlaces

Licencia