int vector[100]; // en principio vector tiene al menos // cien elementos aunque pueda tener más... for (i=0; i<100; i++) vector[i] = vector[i] + 1;

Documentos relacionados
OBJETIVOS ALGUNAS CONSIDERACIONES SOBRE WINDLX

6. PROCESADORES SUPERESCALARES Y VLIW

Introducción a los procesadores ILP (Instruction-Level Parallel)

Ingeniería Informática. Ampliación de Estructura de Computadores. Curso

TEMA 2: PARALELISMO INTERNO EN SISTEMAS COMPUTADORES SEGMENTACION

Procesadores segmentados. El DLX.

Procesadores superescalares. Introducción

COMPUTADORES SEGMENTADOS (DLX)

Ejercicios del Tema 3. Fundamentos de la programación en ensamblador

TEMA 3. PLANIFICACIÓN (SCHEDULING) DE INSTRUCCIONES Arquitectura de Sistemas Paralelos 1. pág 2. direcc MEM

Ingeniería Informática. Ampliación de Estructura de Computadores. Curso Introducción a la segmentación de Instrucciones.

Autor: Longinos Recuero Bustos

1. Objetivo y evolución de los procesadores ILP.

CICLOS DEL PROCESADOR

ARQUITECTURA DE SISTEMAS PARALELOS I. 4º INGENIERÍA INFORMÁTICA. PRÁCTICA 3.

Conceptos de Arquitectura de Computadoras Curso 2015

Práctica 4. DESENROLLADO DE BUCLES.

Arquitectura del CPU. Organización del Computador 1 Verano 2016

Unidad 4 - Procesamiento paralelo. Arquitectura de computadoras. D o c e n t e : E r n e s t o L e a l. E q u i p o : J e s s i c a F i e r r o

Plataformas de soporte computacional: arquitecturas avanzadas,

Bus de direcciones. Bus de datos

Sistema electrónico digital (binario) que procesa datos siguiendo unas instrucciones almacenadas en su memoria

Trabajo Práctico Número 6

TEMA 3 PLANIFICACIÓN O REORDENAMIENTO (SCHEDULING) DE INSTRUCCIONES ÍNDICE

Qué es Pipelining? Es una técnica de implementación en la que se superpone la ejecución de varias instrucciones.

Sección de procesamiento: El camino de datos

Paralelismo en monoprocesadores. Procesadores VLIW

Arquitectura de Computadores. Tema 4 PROCESADORES SEGMENTADOS

SISTEMAS PARALELOS Y DISTRIBUIDOS. 3º GIC. PRÁCTICA 3. PLANIFICACIÓN ESTÁTICA AVANZADA. TMS320C6713

Arquitectura de Computadoras. Clase 5 Posibles soluciones a atascos

TEMA 4. ARQUITECTURA IA-64

Tema 4 (II) El procesador

Ejercicios del tema 4. El procesador

FUNDAMENTOS DE COMPUTADORES 18 de junio de Examen parcial del 2º cuatrimestre.

Introducción a la Computación. Capítulo 10 Repertorio de instrucciones: Características y Funciones

Introducción a la arquitectura de computadores

3 - Arquitectura interna de un up

TEMA III: OPERACIONES CON LOS DATOS

Organización de Computadoras. Clase 7

Ejercicios de jerarquía de memoria

Diseño del procesador MIPS R2000

ARQUITECTURA SPARC: FORMATO DE INSTRUCCIÓN

Comparaciones en lenguaje ensamblador

HISTORIA DEL PIPELINE. Juan Antonio Romano Largo. Arquitectura de Computadores.

Introducción PROCESADOR SUPERESCALAR. Paralelismo. Grado m: en cada ciclo se buscan/decodifican m instrucciones. supes

Plataformas de soporte computacional: arquitecturas avanzadas,

Tecnologías, Organización y Microarquitectura

Pipelining o Segmentación de Instrucciones

Programación de Sistemas. Unidad 1. Programación de Sistemas y Arquitectura de una Computadora

Contenidos. Arquitectura de ordenadores (fundamentos teóricos) Elementos de un ordenador. Periféricos

El Diseño de un Lenguaje Máquina

ARQUITECTURA DE COMPUTADORES. 2º INGENIERÍA INFORMÁTICA. Problemas de Gestión de Memoria.

Introducción a la unidad 4:

Ejercicios Jerarquía de Memoria

202 PROBLEMAS DE ARQUITECTURA DE COMPUTADORES

El nivel ISA (II)! Conjunto de Instrucciones

ISA (Instruction Set Architecture) Arquitectura del conjunto de instrucciones

Explotación del paralelismo a nivel de instrucción

Métodos para escribir algoritmos: Diagramas de Flujo y pseudocódigo

Arquitectura Segmentada: Conceptos básicosb

Arquitectura de Computadores

Tema V Generación de Código

Organización de computadoras. Clase 4. Universidad Nacional de Quilmes. Lic. Martínez Federico

Sistemas con Microprocesadores I

Clase Práctica Memoria Caché

Ensamblador. Introducción. Dpto. Física y Arquitectura de Computadores. Universidad Miguel Hernandez

Procesador. Memoria. Ejemplo de un Procesador: MU0. Instrucciones. Direcciones. Registros. Datos. Instrucciones y datos SETI Tr.

MEMORIA EJERCICIO 1 EJERCICIO 2

TEMA 3: El proceso de compilación, del código fuente al código máquina

Arquitectura de Computadores. Tema 11. Pipelining Avanzado

Tema 1. Introducción a la arquitectura de computadores: diseño, coste y rendimiento

Modelos de Programación Paralela Prof. Gilberto Díaz

Pipeline (Segmentación)

Ejercicios de Arquitectura de Computadoras

PRACTICA 2 Ejercicio 2

TEMA 4. ESTRUCTURAS DE CONTROL

Sistema Operativo. Repaso de Estructura de Computadores. Componentes Hardware. Elementos Básicos

Arquitectura de Computadores II Clase #16

Expresiones Aritméticas. Programación digital I Escuela de Sistemas Facultad de Ingeniería Gilberto Diaz

Tema 2. Diseño del repertorio de instrucciones

Convenciones. Introducción. Unidades principales en la implementación. El procesador: camino de datos y control. Tipos de elementos:

Palabras reservadas de C++ y C. Una palabra reservada no puede declararse como un identificador, esto haría un conflicto entre conectores y funciones.

Relación de Problemas I

Instituto Tecnológico de Morelia

Tema 2 Introducción a la Programación en C.

Tema: Microprocesadores

Procesadores Vectoriales

Arquitectura de Computadores. Apellidos, Nombre:

Todo programa en 'C' consta de una o más funciones, una de las cuales se llama main.

Compiladores: Generación de Código. Pontificia Universidad Javeriana Cali Ingeniería de Sistemas y Computación Prof. María Constanza Pabón

TEMA 2 ILP BASADO EN PLANIFICACIÓN ESTÁTICA DE INSTRUCCIONES ÍNDICE

Construyendo Programas más Complejos

Estructura de Computadores. 1. Ejercicios Resueltos 1.1. Tema 6. La unidad aritmética y lógica

Arquitecturas RISC. Arquitectura de Computadoras y Técnicas Digitales - Mag. Marcelo Tosini Facultad de Ciencias Exactas - UNCPBA

Desde los programas más simples escritos en un lenguaje de programación suelen realizar tres tareas en forma secuencial.

Modos de Direccionamiento

Diferentes Perspectivas de un Ordenador

TEMA II SISTEMAS DE NUMERACIÓN USUALES EN INFORMÁTICA.

Resolución de los Ejercicios de la Hoja 4

Distinguir las diferentes estructuras de repetición utilizadas en problemas con bucles: mientras, repetir mientras, para.

Transcripción:

ARQUITECTURA DE SISTEMAS PARALELOS. 3º INGENIERIA TECNICA EN INFORMATICA DE SISTEMAS. BOLETÍN DE EJERCICIOS DE Introducción al paralelismo. Curso 04/05 (SOLUCIONES) 1. Inicialmente R3 = R2 + 400. como R2 incrementa de 4 en 4 bytes (palabras de 32 bits) entonces se efectuarán 100 iteraciones. La instrucción SUB R4, R3, R2 se puede sustituir por una del tipo SNE (set if not equal). ADDI R3,R2,#400 LOOP: LW R1,0(R2) ADDI R1,R1,#1 SW 0(R2),R1 ADDI R2,R2,#4 SUB R4,R3,R2 BNEZ R4,LOOP ; termina cuando R4 = 0 (100 iteraciones en total) Si se examina el código se puede ver como en cada iteración se lee o carga un elemento E de un vector de números enteros apuntado por R2 y a continuación se le suma un uno. Finalmente se vuelve a escribir el elemento E en el vector en la misma posición. Con estos datos un código en alto nivel escrito en C, que una vez compilado diese lugar al anterior código en ensamblador, tendría el siguiente aspecto: int vector[100]; // en principio vector tiene al menos // cien elementos aunque pueda tener más.... for (i=0; i<100; i++) vector[i] = vector[i] + 1; 2. Se va a ir desglosando instrucción por instrucción los distintos resultados que se van produciendo: XOR R1, R3, R3 ; R1 = 0 ADDUI R1, R1, 0xE4F7 ; R1 = 0000 E4F7 SW 0(R4), R1 ; [R4] = 0000 E4F7 LBU R2, 3(R4) ; R2 = 00 00 00 F7 SGT R1, R2, R1 ; Si (R2 > R1) R1 = 1; Si (00 00 00 F7 > 0000 E4F7) R1 = 1; Si no R1 = 0; Si no R1 = 0; En la representación Big Endian se almacena en los primeros bytes de memoria la parte más significativa del número. Este tipo de representación es seguida por ejemplo por la familia de procesadores INTEL. -1 +0 +1 +2 +3 +4... 00 00 E4 F7...... +4 F7 +3 E4 +2 00 +1 00 +0... -1 3. MOV AX, BX ADD R1, R2, R0 (el registro R0 siempre contiene 0) CMP AX, BX Varias opciones dependiendo de lo que se quiera comparar ya que no hay registro de flags. Por ejemplo: SEL (set equal less) SEL R3, R1, R2 ; Si (R1 <= R2) Entonces R3 = 1; Si no R3 = 0;) NEG [BX] Hay que hacerlo en tres pasos: LW R3, 0(R2) ; SUB R3, R0, R3 (complemento a 2) y SW 0(R2), R3 LOOP ETIQUETA J ETIQUETA INC [BX+DI+100h] Se precisan varias instrucciones RISC: ADD R1, R2, R4 ; ADDI R1, R1, #256 ; LW R3, 0(R1) ; ADDI R3, R3, #1 ; SW 0(R1), R3 1

4. a) Se tienen los siguientes datos: Tipo Instrucción Frecuencia Ciclos de reloj ALU 40% 4 SALTOS 20% 4 MEMORIA 40% 5 Además se dice que el periodo de reloj es de 10 ns y se pregunta cuál es la duración media de una instrucción. Por lo tanto habrá que hacer una media ponderada con las frecuencias y los ciclos de las diferentes instrucciones: T instr. = CPI * T ciclo = ( 0,4 * 4 + 0,2 * 4 + 0,4 * 5 ) * 10 = 44 ns b) La mejora será S = T no segmentado / T segmentado Se dice que debido a complicaciones de la arquitectura segmentada se necesita un nanosegundo más en el ciclo de reloj. Por lo tanto en este caso el ciclo de reloj es de 11 ns. Así pues se tendrá que: S = 44 / 11 = 4, es decir una mejora en el rendimiento de hasta un 300 % más. 5. Se dispone de un procesador segmentado de 4 etapas: A B C D Los saltos condicionales se toman al final de tercer ciclo y los incondicionales al final del segundo. Además los saltos condicionales suponen un 15% del total de instrucciones (de los que el 55% se toman) y los incondicionales un 5%. Hay que hacer un análisis de lo que ocurre en los saltos: 1) Saltos condicionales: I : A B C D I +1 A - A B C D Se toma salto: 2 ciclos I+2 A - B C D No se toma salto: 1 ciclo CPB = 1 ó 2 2) Saltos incondicionales: I : A B C D I+1 A A B C D Se toma siempre el salto: 1 ciclo CPB = 1 Así pues el CPB (ciclos por bloqueo) global será: CPB global = % instrucciones de s.incondicional * CPB s.incondicionales + % instrucciones de s.condicional * CPB s.condicionales = 0,05*1 + 0,15*(0,55*2 + 0,45*1) = 0,28 (28% más lenta) Comparando con la máquina secuencial la relación entre la eficiencia de ambas es : S = CPI no seg. / (CPI ideal seg. + CPB) = 4 / 1,28 = 3,12 6. Lo que se quiere ver es el la relación entre el tiempo que tarda un procesador en ejecutar un programa respecto a otro. El tiempo de ejecución es: T = N (nº instrucciones programa) x CPI (ciclos reloj por instrucción) x t (ciclo de máquina o reloj) La relación que se quiere comprobar es S,donde S = T * /T (siendo T * el tiempo del DLX modificado y T el tiempo de un DLX normal) N = N * ya que se suponen que los programas ejecutados por ambos procesadores son los mismos y por lo tanto el nº de instrucciones es igual. t * = 1,5 x t (porque el DLX * tiene un reloj un 50% más lento) 2

El CPI del DLX normal (CPI dlx ) es 1,2 ya que ejecuta una instrucción por ciclo más un 0,2 ciclos de media por bloqueos (no se alcanza nunca el ideal de una instrucción por ciclo). La pregunta clave es el CPI del DLX *. Para ello hay que examinar los posibles bloqueos que se producen en un procesador pero no en otro. Se puede ver que todo es igual salvo un caso especial en las dependencias de control (saltos). Esquemáticamente: DATOS: SALTOS: DLX DLX * Aritmética IF ID EX M WB IF IDX M WB Aritmética IF ID EX M WB IF IDX M WB Load/carga IF ID EX M WB IF IDX M WB Aritmética IF ID - EX M WB IF - IDX M WB IF ID EX M WB IF IDX M WB IF IF ID EX M WB IF IF IDX M WB En el DLX normal y corriente se pierde un ciclo en la instrucción de salto si la instrucción anterior crea dependencia. Esto es debido a que en la fase ID del salto se evalúa la condición de salto al final del ciclo. Esto no es posible hacerlo si al final de la fase EX de la instrucción anterior, se calcula el valor de un registro cuyo valor debía de tenerse listo justo al principio de la fase ID del salto. Este caso sin embargo no ocurre en el DLX * ya que ID y EX se fusionan en un único ciclo y se puede hacer entonces un bypass normal y corriente. Esquemáticamente se puede ver más claramente esta dependencia de control que se produce con algunas instrucciones de salto: DLX DLX * SEQ R2,R1,R6 IF ID EX M WB IF IDX M WB BEQZ R2 IF - ID EX M WB IF IDX M WB Por lo tanto el CPI * del DLX * será el mismo CPI del DLX normal MENOS el CPI de los casos en los que se produce la anterior dependencia que se acaba de describir. A esta ocurrencia de casos se la llama "f (frecuencia de casos ó f dependencia_control ). Así pues la relación entre ambos CPI-s es: CPI * / CPI = ( CPI dlx - 1 ciclo_bloqueo x f dependencia_control ) / CPI dlx = (1,2 - f) /1,2 Volviendo a nuestra fórmula inicial de S = T * / T se tiene: S = T * / T = (N * x (1,2 - f) x 1,5) / (N x 1,2 x 1) = (1,2 - f) x 1,5 / 1,2 Haciendo S = 1 (es decir, ambos procesadores ejecutan el mismo programa en el mismo periodo de tiempo) tendremos la "frontera" a partir de cual uno compensa más que el otro. Resolviendo se tiene f = 0,4 y por lo tanto si hubiese más de un 40% de las dependencias de control que se ha visto en nuestro programa, entonces el DLX * sería mejor que el DLX normal. Eso es algo que en circunstancias normales no va a ocurrir ya que para empezar el 40% de las instrucciones tendrían que ser de saltos tendrían que ser saltos condicionales y para acabar además todas ellas deberían dar dependencias. 7. a) El diagrama de ejecución sin desvíos en la primera iteración es el siguiente: ADDI R2,R0,#40 IF ID EX M WB Suma: LW R1,0x100(R2) IF - - ID EX M WB ADD R5,R5,R1 IF - - ID EX M WB SUBI R2,R2,#4 IF ID EX M WB BNEZ R2,SUMA IF - - ID EX M WB Suma: LW R1,0x100(R2) IF IF ID EX 3

En la segunda y siguientes iteraciones el diagrama de ejecución será igual salvo la primera iteración: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Suma: LW R1,0x100(R2) IF ID EX M WB ADD R5,R5,R1 IF - - ID EX M WB SUBI R2,R2,#4 IF ID EX M WB BNEZ R2,SUMA IF - - ID EX M WB Suma: LW R1,0x100(R2) IF IF ID EX M WB El bucle se repite 10 veces y hay que hacer distinción entre la primera iteración, la última y las otras 8. T = T primera + T siguientes = 12 ciclos + 9 ciclos * 8 iteraciones + 12 ciclos = 96 ciclos b) Con bypass/cortocircuitos: ADDI R2,R0,#40 IF ID EX M WB Suma: LW R1,0x100(R2) IF ID EX M WB ADD R5,R5,R1 IF ID - EX M WB SUBI R2,R2,#4 IF - ID EX M WB BNEZ R2,SUMA IF - ID EX M WB Suma: LW R1,0x100(R2) IF IF ID EX T = T primera + T siguientes = 8 ciclos + 7 ciclos * 8 iteraciones + 10 ciclos = 74 ciclos c) Reordenando sin bypass: ADDI R2,R0,#40 IF ID EX M WB Suma: LW R1,0x100(R2) IF - - ID EX M WB SUBI R2,R2,#4 IF ID EX M WB ADD R5,R5,R1 IF - ID EX M WB BNEZ R2,SUMA - IF ID EX M WB Suma: LW R1,0x100(R2) IF IF ID EX M WB T = T primera + T siguientes = 9 ciclos + 6 ciclos * 8 iteraciones + 9 = 66 ciclos d) Reordenando y con bypass: no siempre la reordenación es la misma y podría haberse diferenciado de la anterior. En este caso si coinciden. ADDI R2,R0,#40 IF ID EX M WB Suma: LW R1,0x100(R2) IF ID EX M WB SUBI R2,R2,#4 IF ID EX M WB ADD R5,R5,R1 IF ID EX M WB BNEZ R2,SUMA IF ID EX M WB Suma: LW R1,0x100(R2) IF IF ID EX M WB T = T primera + T siguientes = 6 ciclos + 5 ciclos * 8 iteraciones + 6 = 54 ciclos 4

8. a) El diagrama de ejecución sin desvíos en la primera iteración es el siguiente: 16 17 ADDI R3,R2,#400 IF ID EX M WB LOOP:LW R1,0(R2) IF ID EX M WB ADDI R1,R1,#1 IF - - ID EX M WB SW 0(R2),R1 IF - - ID EX M WB ADDI R2,R2,#4 IF ID EX M WB SUB R4,R3,R2 IF - - ID EX M WB BNEZ R4,LOOP IF - - ID EX LOOP:LW R1,0(R2) IF IF En la segunda y siguientes iteraciones el diagrama de ejecución será igual salvo la primera iteración: 16 LOOP: LW R1,0(R2) IF ID EX M WB ADDI R1,R1,#1 IF - - ID EX M WB SW 0(R2),R1 IF - - ID EX M WB ADDI R2,R2,#4 IF ID EX M WB SUB R4,R3,R2 IF - - ID EX M WB BNEZ R4,LOOP IF - - ID EX LOOP:LW R1,0(R2) IF IF El bucle se repite 100 veces y hay que hacer distinción entre la primera iteración y las otras 99 siguientes. T = T primera + T siguientes = 16 ciclos + 15 ciclos * 99 iteraciones = 1501 ciclos b) Con bypass/cortocircuitos: ADDI R3,R2,#400 IF ID EX M WB LOOP:LW R1,0(R2) IF ID EX M WB ADDI R1,R1,#1 IF ID - EX M WB SW 0(R2),R1 IF - ID EX M WB ADDI R2,R2,#4 IF ID EX M WB SUB R4,R3,R2 IF ID EX M WB BNEZ R4,LOOP IF - ID EX M WB LOOP:LW R1,0(R2) - IF IF ID EX M WB T = T primera + T siguientes = 10 ciclos + 9 ciclos * 99 iteraciones = 901 ciclos Una forma alternativa de calcular el número total de ciclos es calculando el CPB. A continuación se calcularía el número total de ciclos: T = N * CPI = N * (1 + CPB), donde N es el número total de instrucciones que se ejecutan. En este caso 601 instrucciones (6*100 más la instrucción ADDI inicial). CPB = CPB datos + CPB control = (2*100 iteraciones / 601 instrucciones) + (1*100 iteraciones / 601 instrucciones) = 300 / 601 T = N * CPI = N * (1 + CPB) = 601 * (1 + 300 /601) = 901 ciclos c) Reordenando sin bypass: ADDI R3,R2,#400 IF ID EX M WB LOOP:LW R1,0(R2) IF ID EX M WB ADDI R2,R2,#4 IF ID EX M WB ADDI R1,R1,#1 IF - ID EX M WB SUB R4,R3,R2 IF ID EX M WB SW -4(R2),R1 IF - ID EX M WB BNEZ R4,LOOP IF ID EX M WB LOOP:LW R1,0(R2) IF IF ID EX M WB T = T primera + T siguientes = 10 ciclos + 9 ciclos * 99 iteraciones = 901 ciclos 5

Hay que fijarse que aunque reordenando las instrucciones no se han conseguido eliminar todos los bloqueos se ha conseguido un rendimiento igual al obtenido en el anterior caso con bypass. d) Reordenando y con bypass: ahora con el bypass es posible eliminar los anteriores bloqueos. ADDI R3,R2,#400 IF ID EX M WB LOOP:LW R1,0(R2) IF ID EX M WB ADDI R2,R2,#4 IF ID EX M WB ADDI R1,R1,#1 IF ID EX M WB SUB R4,R3,R2 IF ID EX M WB SW -4(R2),R1 IF ID EX M WB BNEZ R4,LOOP IF ID EX M WB LOOP:LW R1,0(R2) IF IF ID EX M WB T = T primera + T siguientes = 8 ciclos + 7 ciclos * 99 iteraciones = 701 ciclos El número de ciclos es menos de la mitad de ciclos empleados en el primer caso! 9. La única instrucción que se puede reubicar en el hueco dejado por la instrucción de salto es la ADD R5, R5, R1 ya que el resto alteran la lógica del programa. El código del programa en ese caso quedaría entonces de la siguiente forma: ADDI R2,R0,#40 SUMA: LW R1,0x100(R2) SUBI R2,R2,#4 RBNEZ R2,SUMA ADD R5,R5,R1 Sin embargo del ejercicio 7 se sabe que existe una dependencia de datos entre SUBI y la instrucción de salto debido a R2. Por ello se ha de poner la instrucción de SUBI antes de la instrucción LW. ADDI R2,R0,#40 IF ID EX M WB Suma: SUBI R2,R2,#4 IF ID EX M WB LW R1,0x104(R2) IF ID EX M WB RBNEZ R2,SUMA IF ID EX M WB ADD R5,R5,R1 IF ID EX M WB Suma: SUBI R2,R2,#4 IF ID EX M WB T = T primera + T siguientes = 5 ciclos + 4 ciclos * 9 iteraciones = 41 ciclos. Esto quiere decir que se tiene 4 ciclos por iteración, lo cual es perfecto ya que en cada iteración se ejecutan 4 instrucciones. 10. En principio la alternativa de usar la técnica de salto retrasado/retardado se puede aplicar con el salto incondicional (J) y la instrucción ADDI ya que esa instrucción se habrá necesariamente de ejecutar junto con el salto. Por ello se podría pensar en una nueva instrucción RJMP que aplique salto retardado. LW R1, 0(R2) ADD R3,R0,R0 ETIQ: SUBI R1,R1,#4 BEQZ R1,FIN ADDI R3,R3,#1 J ETIQ FIN: LW R1, 0(R2) ADD R3,R0,R0 ETIQ: SUBI R1,R1,#4 RBEQZ R1,FIN _ RJ ETIQ ADDI R3,R3,#1 FIN: 6

No se podría aplicar el salto retardado con la otra instrucción de salto ya que no queda ninguna otra instrucción libre puesto que el resto de instrucciones están antes de la etiqueta de salto (ETQ) o después de etiqueta de FIN. La instrucción SUBI R1,R1,#4 aunque está dentro del bucle debido a la lógica del programa se ha de ejecutar antes del salto condicional. 11. Se pide el CPB control, o sea, el número medio de ciclos que se pierden por instrucción debido a las instrucciones de saltos. Se dispone de una BTB que en caso de acierto (predicción correcta) no se pierde ningún ciclo. Hay que tener claro que el DLX por defecto no tiene BTB y aprovecha la fase IF de la siguiente instrucción buscada en el caso de que finalmente el salto no se tome, es decir, después de una instrucción de salto se busca siempre la siguiente instrucción. Cuando en base a la evaluación de la condición haya que anular la instrucción, entonces se anula la fase IF de la siguiente instrucción y se busca la nueva instrucción. En todo este proceso se pierde un ciclo de reloj. En el enunciado se proporcionan un montón de datos estadísticos respecto al porcentaje de aciertos en la BTB y los saltos que se toman y los que no. En base a estos datos se va a calcular el CPB control. Cuando se produce un acierto en la BTB no producen bloqueos. Durante la fase IF de la instrucción de salto se accede a la BTB y se obtiene la dirección de la siguiente instrucción: i: SALTO IF ID EX M WB j: IF ID EX M WB Instrucción salto correcta Cuales serían los casos en los que se producen bloqueos? Dos casos: 1 - La búsqueda en la BTB es fructuosa (probabilidad 0,9) PERO luego resulta que se falla en la predicción (probabilidad 1-0,9=0,1). En este caso la probabilidad es 0,9*0,1. En este caso cuántos ciclos se pierden? Dos ciclos. El primero porque el IF que ha entrado en pipeline es el de una instrucción errónea (hay que anularlo) y el segundo porque hay que actualizar la BTB. Por lo tanto en este 2º ciclo de bloqueo hay que actualizar la BTB antes de buscar el siguiente IF. Por lo tanto CPB = 0,9 x 0,1 x 2 ciclos. i: SALTO IF ID EX M WB i+1: IF - - - - Sig. Instrucción anulada i+2: - - - - - Actualización BTB j: IF ID EX M WB Instrucción salto correcta 2 - La búsqueda en la BTB NO es fructuosa (probabilidad 1-0,9=0,1) y además luego se salta (probabilidad 0,6). En este caso la probabilidad es 0,6*0,1. El número de ciclos que se pierden en este tipo de casos es también 2 y la explicación es análoga el del primer caso. Por lo tanto CPB = 0,1 x 0,6 x 2 ciclos. Finalmente uniendo 1 y 2 queda que: CPB control = 0,9 x 0,1 x 2 ciclos + 0,1 x 0,6 x 2 ciclos = 0,3. Es decir una instrucción de control supone 1,3 ciclos de media. Como se dice en el enunciado sólo los saltos tomados actualizan la BTB, por lo tanto en caso de fallo (no está en la BTB) si no se toma el salto el número de ciclos de bloqueo son cero. Si se compara con un DLX normal (sin BTB) se tendría que: CPB control = 0,6 (porcentaje saltos tomados) x 1 ciclo (anular la instrucción siguiente que se busca por defecto y buscar la instrucción de salto) = 0,6. Es decir una instrucción de control supone 1,6 ciclos de media. 7

12. Dado el siguiente programa: for(i=1;i<=100;i=i+1) { A Y[i]=X[i]/c; B X[i]=X[i]+c; C Z[i]=Y[i]+c; D Y[i]=c-Y[i]; } Se producen las siguientes dependencias: - Dependencias reales (RAW): A-C, A-D - Antidependencias (WAR): B-A, D-C - Dependencias de salida (WAW): A-D El renombrado para evitar posibles antidependencias y dependencias de salida quedaría como sigue: for(i=1;i<=100;i=i+1) { A T[i]=X[i]/c; B X1[i]=X[i]+c; C Z[i]=T[i]+c; D Y[i]=c-T[i]; } 13. Primero se va a ver el CPI obtenido así como las dependencias y bloqueos que se producen con un DLX escalar. Esto también ayudará a determinar más tarde el número de instrucciones que se deberán intercalar entre los distintos tipos de instrucciones para evitar los bloqueos: 1 2 3 4 5 6 7 8 9 10 11 12 LOOP: LW R2, 0(R1) IF ID EX M WB ADD R4, R2, R3 IF ID - EX M WB SW 0(R1), R4 IF - ID EX M WB SUBI R1, R1, #4 - IF ID EX M WB BNEZ R1, LOOP IF - ID EX M WB LW R2, 0(R1) IF IF ID EX M Se producen dos bloqueos debidos a dependencias de datos y otro más debido a dependencias de control (instrucción de salto). Por lo tanto el número total de ciclos necesarios para ejecutar el bucle será: (5 + CPB datos + CPB control ) * Nº iteraciones = (5 + 2 + 1)*N = 8N Para calcular el CPI se tienen: 8 ciclos por iteración para 5 instrucciones CPI = 8/5 = 1,6 Ahora se va a pasar a desenrollar el anterior programa para poder aprovechar mejor las características superescalares del procesador. Hay que tener en cuenta que se van a poder emitir dos instrucciones al mismo tiempo (una de tipo ALU o salto y la otra de acceso a memoria) por lo que las dependencias a estudiar ya no van a ser solamente entre instrucciones individuales sino entre pares de instrucciones. Cuántas veces hay que desenrollar el bucle? Como en principio no se dice nada se empezará lo más sencillo y se desenrollará el bucle dos veces. Primero se duplica el cuerpo del bucle dos veces y luego se reordenan las instrucciones: 8

LW R2, 0(R1) ADD R4, R2, R3 SW 0(R1), R4 SUBI R1, R1, #4 LW R5, 0(R1) ADD R6, R5, R3 SW 0(R1), R6 SUBI R1, R1, #4 BNEZ R1, LOOP NOP LW R2, 0(R1) ADD R4, R2, R3 SW 0(R1), R4 LW R5, -4(R1) ADD R6, R5, R3 SW -4(R1), R6 SUBI R1, R1, #8 BNEZ R1, LOOP 1 LW R2, 0(R1) NOP 2 LW R5, -4(R1) SUBI R1, R1, #8 3 ADD R4, R2, R3 NOP 4 ADD R6, R5, R3 SW 8(R1), R4 5 SW 4(R1), R6 BNEZ R1, LOOP Como se puede ver no se aprovecha todo el paralelismo que nos ofrece el procesador. Así en las dos emisiones de los pares de instrucciones 1 y 3 se han de recurrir a los NOP-s ya que no es posible buscar instrucciones con las que se puedan rellenar los huecos sobrantes. Hay que fijarse también en las instrucciones de acceso a memoria (LW y SW) se han tenido que modificar los índices para el cálculo de la dirección. Con este resultado ya podemos calcular el número de ciclos necesario para ejecutar este programa desenrollado. Se tiene ahora que CPB datos = 0 y CPB control = 1. Ciclos bucle: (5 + CPB datos + CPB control ) * Nº iteraciones / 2 = (5 + 0 + 1)*N/2 = 3N Para calcular el CPI se tienen: 6 ciclos para 8 instrucciones CPI = 6/8 = 0,75 Este CPI todavía se aleja algo del ideal de 0,5 es decir, una instrucción se ejecuta en medio ciclo o lo que es lo mismo: en un ciclo de reloj se ejecutan dos instrucciones. Sin embargo el CPI obtenido si que es claramente mejor que el 1,6 obtenido con el DLX escalar. Si se hubiese hecho loop-unrolling de 3 para aprovechar más aún el paralelismo el código hubiese quedado tal como sigue: LW R2, 0(R1) ADD R4, R2, R3 SW 0(R1), R4 SUBI R1, R1, #4 LW R5, 0(R1) ADD R6, R5, R3 SW 0(R1), R6 SUBI R1, R1, #4 LW R8, 0(R1) ADD R10, R8, R3 SW 0(R1), R10 SUBI R1, R1, #4 BNEZ R1, LOOP NOP LW R2, 0(R1) ADD R4, R2, R3 SW 0(R1), R4 LW R5, -4(R1) ADD R6, R5, R3 SW -4(R1), R6 LW R8, -8(R1) ADD R10, R8, R3 SW -8(R1), R10 SUBI R1, R1, #12 BNEZ R1, LOOP 1 LW R2, 0(R1) NOP 2 LW R5, -4(R1) SUBI R1, R1, #12 3 LW R8, 4(R1) ADD R4, R2, R3 4 SW 12(R1), R4 ADD R7, R5, R3 5 SW 8(R1), R7 ADD R10, R8, R3 6 SW 4(R1), R10 BNEZ R1, LOOP Con este nuevo código se tiene que CPB datos = 0 y CPB control = 1. Por otro lado el bucle tendrá 6 + CPB control = 7 ciclos. Como se tratan 3 elementos en cada iteración entonces en total el programa tendrá 7*N/3 siendo N el número total de elementos del vector. El CPI será 7 ciclos / 11 instrucciones = 0,63 aproximadamente, lo cual está todavía más cerca del ideal 0,5. 9

14. a) Para saber cuál es el número de sumadores mínimos necesarios hay que examinar las diferentes etapas del pipeline para comprobar las funciones realizadas y así deducir si habrá que utilizar o no sumadores/alu-s. IF Un sumador para calcular la dirección de la siguiente instrucción a ejecutar RF Ningún sumador. Sólo se decodifica y se buscan los operandos ALU1 Un sumador para hacer los cálculos de la dirección efectiva: REG. + OFFSET MEM Ningún sumador. Sólo acceso a memoria ALU2 Un sumador/alu para realizar las operaciones aritmético-lógicas y se evalúan las condiciones de salto WB Ningún sumador. En principio sólo se escribe en los registros En total se necesitan 3 sumadores/alu-s como mínimo. b) Para determinar todos los posibles desvíos se hay que examinar el contenido del pipeline durante una ejecución de instrucciones cualquiera y ver dónde se podrían aplicar bypass. 1 IF RF ALU1 MEM ALU2 WB 2 IF RF ALU1 MEM ALU2 WB 3 IF RF ALU1 MEM ALU2 WB 4 IF RF ALU1 MEM ALU2 WB 5 IF RF ALU1 MEM ALU2 WB - Centrándose en las instrucciones 1 y 2 se puede ver que no tendría mucho sentido hacer un cortocircuito entre las fases ALU1 ya que sólo se hacen cálculos de direcciones efectivas y por lo tanto no se calculan valores que luego vayan a ser escritos en registros. Si podría haber cortocircuito entre las fases MEM ya que se podría leer un dato que previamente acaba de ser cargado de memoria. 1: LW R6, 0(R5) IF RF ALU1 MEM ALU2 WB 2: SW 0(R8), R6 IF RF ALU1 MEM ALU2 WB Sin embargo, algunos de estos tipos de cortocircuitos se pueden evitar mediante técnicas software consistentes en optimización de código. Para ello la dirección efectiva tendría que coincidir. Ejemplo: 1: LW R6, 0(R5) IF RF ALU1 MEM ALU2 WB 2: SW 0(R5), R6 IF RF ALU1 MEM ALU2 WB LW R6, 0(R5) NOP (o directamente quitarla) - Más claro parece el cortocircuito entre las fases ALU2. Aquí un valor calculado en una instrucción anterior se puede utilizar en la siguiente. Ejemplo: 1: ADD R5, R6, R7 IF RF ALU1 MEM ALU2 WB 2: MUL R8, R5, R9 IF RF ALU1 MEM ALU2 WB - No hay más posibilidades entre las instrucciones 1 y 2. Se van a analizar ahora los posibles cortocircuitos entre las instrucciones 1 y 3. Por la misma razón que en el anterior grupo de cortocircuitos no tiene sentido realizar bypass entre ALU1 y RF ya que en ALU1 no se escribe ningún valor final en un registro y RF sólo se leen operandos. Si tiene sentido realizar un cortocircuito entre las fases MEM y ALU1: un valor/operando cargado de memoria puede ser utilizado a continuación. Ejemplo: 1: LW R7, 0(R5) IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: ADD R6, R7, R1 IF RF ALU1 MEM ALU2 WB - También existe la posibilidad de realizar un bypass entre las fases ALU2 y MEM. En este caso un valor u operando previamente calculado puede ser a continuación almacenado en memoria. Viene a ser el caso contrario al del anterior. Ejemplo: 1: ADD R7, R6, R1 IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: SW 0(R5), R7 IF RF ALU1 MEM ALU2 WB 10

- Finalizados todos los posibles cortocircuitos entre la instrucción 1 y 3 queda ver si es posible alguno entre las instrucciones 1 y 4. Como se ha hecho antes se va a ir examinando la instrucción 4 en todas sus fases yendo de izquierda a derecha. Así en la fase RF existe la posibilidad de necesitar un operando cargado en la primera instrucción. Ejemplo: 1: LW R7, 0(R5) IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: IF RF ALU1 MEM ALU2 WB 4: ADD R7, R7, R1 IF RF ALU1 MEM ALU2 WB - También se puede dar el caso de un cortocircuito entre las fases ALU2 y ALU1 de la misma forma parecida a como se ha visto anteriormente. Este cortocircuito sólo se activaría cuando el contenido del registro de la cuarta instrucción se usase para calcular una dirección efectiva. Ejemplo: 1: ADD R7, R6, R5 IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: IF RF ALU1 MEM ALU2 WB 4: MUL R8, R4, 0(R7) IF RF ALU1 MEM ALU2 WB - Por ultimo falta ver los posibles cortocircuitos que se pueden aplicar cuando las instrucciones están separadas por otras tres instrucciones en medio. Realmente sólo hay una posibilidad: Cuando en el cuarta instrucción se necesita un operando que proviene de una operación aritmética de la primera instrucción. Ejemplo: 1: ADD R8, R6, R5 IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: IF RF ALU1 MEM ALU2 WB 4: IF RF ALU1 MEM ALU2 WB 5: ADD R1, R8, R7 IF RF ALU1 MEM ALU2 WB Sin embargo esta bypass se puede evitar suponiendo que este procesador hace lo mismo que el DLX, es decir, en el primer medio ciclo de la fase WB se escribe y en el segundo medio ciclo de la etapa RF se lee. c) Para determinar todos los posibles bloqueos de datos no evitables se va a hacer un estudio similar al anterior. Se van a ir cogiendo pares de instrucciones del siguiente esquema de ejecución, siendo la instrucción 1 siempre el primer miembro del par. Todas las dependencias de datos van a ser del tipo reales (RAW) debido a que se escribe siempre en la fase última WB y todas las instrucciones tienen el mismo número de fases. Por lo tanto, las dependencias de tipo WAW y WAR no van a aparecer. 1 IF RF ALU1 MEM ALU2 WB 2 IF RF ALU1 MEM ALU2 WB 3 IF RF ALU1 MEM ALU2 WB 4 IF RF ALU1 MEM ALU2 WB 5 IF RF ALU1 MEM ALU2 WB - Se empieza por la 1ª y 2ª instrucción. Un primer bloqueo no evitable se produce cuando existe una dependencia real (RAW) entre las fases MEM y ALU1. En este caso la 2ª instrucción necesita leer un operando que todavía no se ha cargado de memoria. Este tipo de bloqueos son inevitables y prácticamente los únicos bloqueos de datos presentes en el DLX. Un detalle bastante importante es que ahora la 2ª instrucción debe ser también otra de acceso a memoria y no una instrucción aritmética entre registros. Ejemplo: 1: LW R8, 0(R7) IF RF ALU1 MEM ALU2 WB 2: DIV R6, R6, 0(R8) IF - RF ALU1 MEM ALU2 WB 11

- Otro posible bloqueo surge cuando la segunda instrucción es una instrucción de acceso a memoria y necesita un operando calculado en una operación de la 1ª instrucción. Ejemplo: 1: ADD R6, R7, R4 IF RF ALU1 MEM ALU2 WB 2: SW 0(R8), R6 IF - RF ALU1 MEM ALU2 WB - Se van a estudiar ahora los posibles bloqueos existentes cuando hay una instrucción en medio. En este caso un primer bloqueo surge cuando en la 3ª instrucción se necesita un operando cargado en la 1ª instrucción. Ejemplo: 1: ADD R8, R7, R5 IF RF ALU1 MEM ALU2 WB 2: IF RF ALU1 MEM ALU2 WB 3: ADD R6, R5, 0(R8) IF RF - ALU1 MEM ALU2 WB d) Para determinar los bloqueos de control hay primero que estudiar cómo y en qué fase se produce el salto. La dirección del salto se calcula en la fase ALU1 y se resuelve la condición en ALU2. Como para resolver la condición disponemos de una ALU completa las instrucciones de salto condicional pueden ser más complejas: cbeq R1,R2, Etiq El diagrama de fases en caso de salto quedaría tal como sigue: Salto no tomado (apostando por salto no tomado): i: salto IF RF ALU1 MEM ALU2 WB i+1: IF RF ALU1 - MEM ALU2 WB i+2: IF RF - ALU1 MEM ALU2 WB i+3: IF - RF ALU1 MEM ALU2 WB i+4: IF RF ALU1 MEM ALU2 WB Salto tomado (apostando por salto no tomado): i: salto IF RF ALU1 MEM ALU2 WB i+1: IF RF ALU1 - IF RF ALU1 MEM ALU2 WB i+2: IF RF - - IF RF ALU1 MEM ALU2 WB i+3: IF - - - IF RF ALU1 MEM ALU2 WB i+4: IF RF ALU1 MEM ALU2 WB En las etapas IF, RF y ALU1 no se modifica el estado de la máquina por lo que pueden ejecutarse sin saber si se va a tomar el salto o no. Si el salto no se toma pueden aprovecharse las etapas realizadas, produciéndose un único ciclo de bloqueo. Si por el contrario no se toma hay que repetir las etapas realizadas antes de conocer la condición de salto produciéndose cuatro ciclo de bloqueo. Al contrario que en el DLX donde se calculaba la dirección de salto y la condición en la misma etapa, aquí si tiene sentido apostar por el salto tomado. Esto es debido a que la dirección de salto se calcula antes que la condición. Se deja al alumno como tarea estudiar los bloqueos de control en caso de apuesta por salto tomado. 12

15. a) Si se ejecutase tal cual el código generado en un DLX normal se activarían los siguientes cortocircuitos y se producirían los siguientes bloqueos. (Nota: en el esquema siguiente y en los posteriores esquemas faltan las tres primeras instrucciones. La 2ª y la 3ª producen un cortocircuito entre las fases EX) 16 17 1 ADDI R9, R0, #40 IF ID EX M W 2 MOVI2FP F2, R9 IF ID EX M W 3 loop:ld R1, 0(R2) IF ID EX M W 4 ADD R3, R1, R1 IF - ID EX M W 5 SW 0(R2), R3 IF ID EX M W 6 SUBI R2, R2, #4 IF ID EX M W 7 LF F1, 0(F2) IF ID EX M W 8 MULTF F3, F1, F1 IF - ID EX M W 9 SF 0(F2), F3 IF ID EX M W 10 SUBF F2, F2, F4 IF ID EX M W 11 BNEQ R2, bucle IF ID EX M W 12 loop:ld R1, 0(R2) IF IF ID EX Como se puede ver sólo se producen dos bloqueos debidos a dependencias de datos tipo RAW (dependencias reales). Estas dependencias se pueden eliminar intercalando una instrucción entre las instrucciones de load y la siguiente instrucción aritmética. Por ejemplo: para el caso del tratamiento del elemento en vector_int se podría intercalar la instrucción 6ª entre la 3ª y la 4ª. Además habría que modificar el cálculo de la dirección relativa al acceder al elemento del vector en la instrucción 5ª de forma que quede algo así como: SW 4(R2), R3 En la primera iteración se ejecutan 14 instrucciones en 17 ciclos de reloj para tratar los primeros elementos de los vectores: 5 para la inicialización y 12 para el cuerpo. En la segunda y sucesivas iteraciones se ejecutan 9 instrucciones en 12 ciclos por cada elemento a tratar de vector_int y vector_float. Asimismo se activan 6 cortocircuitos contando con el que hay entre la 2ª y 3ª instrucción (de las que no aparecen en el esquema) Teniendo en cuenta que se tiene un superescalar de dos habrá que agrupar conjuntamente instrucciones de tipo entero e instrucciones de tipo flotante intentando evitar ese tipo de bloqueo anterior debido a la dependencia de datos RAW. Como el tratamiento del elemento en vector_int y en vector_float es independiente podemos emparejar cada load de tipo integer con el load de tipo float. Lo mismo para las instrucciones aritméticas y los stores. Así una reordenación del código óptima (sin bloqueos) sería la siguiente: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 ADDI R9, R0, #40 IF ID EX M W 1 MOVI2FP F2, R9 IF ID EX M W 2 loop:ld R1, 0(R2) IF ID EX M W 2 LF F1, 0(F2) IF ID EX M W 3 SUBI R2, R2, #4 IF ID EX M W 3 SUBF F2, F2, F4 IF ID EX M W 4 ADD R3, R1, R1 IF ID EX M W 4 MULTF F3, F1, F1 IF ID EX M W 5 SW 0(R2), R3 IF ID EX M W 5 SF 0(F2), F3 IF ID EX M W 6 BNEQ R2, bucle IF ID EX M W 6 NOP (instr. Float) - - - - - 7 loop:ld R1, 0(R2) IF IF ID EX M W 7 LF F1, 0(F2) IF IF ID EX M W En el esquema falta el tratamiento de las 3 primeras instrucciones que se realizarían en dos pasos: primero la instrucción ADDI R2, R0, #40 y después las instrucciones ADDI R9, R0, #4 y MOVI2FP F4, R9 en paralelo. Como se puede ver ahora, se ejecutan 14 instrucciones en 10 ciclos de reloj para tratar los primeros elementos de los vectores: 3 para la inicialización y 7 para el cuerpo del bucle. En la segunda y sucesivas iteraciones se ejecutan 9 instrucciones en 7 ciclos por cada elemento a tratar de vector_int y vector_float lo cuál representa una mejora en el rendimiento (S) del 12 ciclos /7 ciclos = 1,71 respecto 13

de un procesador DLX tradicional. Otra diferencia respecto al anterior caso es que ahora se activan 5 cortocircuitos en vez de 6. b) Hay dos formas de plantear la solución de este ejercicio. Una es suponiendo que el VLIW está segmentado y otra suponiendo que NO es segmentado. Si se supone que NO es segmentado en cada instrucción se ejecuta una operación completa de cada tipo. De esta forma nunca hay dependencias entre operaciones del mismo tipo en instrucciones continuas y por lo tanto las instrucciones pueden ser emitidas una detrás de otra sin mayores problemas. El problema es que cada instrucción necesitaría 5 ciclos (IF, ID, EX, M, WB) porque no se aprovecha la segmentación. En el caso de que exista una segmentación completa (pipeline) el sistema será más eficiente pero hay que tener en cuenta las posibles dependencias entre operaciones de un mismo tipo, igual que cuando se tenía un DLX segmentado SIN cortocircuitos. Para ello el compilador deberá insertar las correspondientes instrucciones NOP donde proceda. En el caso del DLX segmentado era el propio procesador el que por hardware bloqueaba el pipeline. Esta última solución es la que se detalla a continuación. Teniendo como referencia el código anterior se puede ver que, ahora al no tener cortocircuitos entre una instrucción load y la siguiente instrucción que utilice el operando del load, deberá al menos haber dos ciclos de reloj. Lo mismo entre una operación aritmética y la operación store que almacena el resultado anteriormente calculado. INSTR. 1 acceso a memoria para flotantes 1 acceso a memoria para enteros 1 operaciones aritméticas para flotantes 1 operaciones aritméticas para enteros 1 para saltos 1 ADDI R2, R0, #40 2 ADDI R9, R0, #4 3 4 5 MOVI2FP F4, R9 ADDI R9, R0, #40 6 7 8 MOVI2FP F2, R9 9 10 11 LF F1, 0(F2) LD R1, 0(R2) 12 SUBF F2, F2, F4 SUBI R2, R2, #4 13 14 MULTF F3, F1, F1 ADD R3, R1, R1 15 16 17 SF -4(F2), F3 SW -4(R2), R3 BNEQ R2, bucle 18 (19) LF F1, 0(F2) LD R1, 0(R2) Un hecho curioso y a tener en cuenta es que ahora las operaciones que decrementan los punteros a los vectores (SUBF F2, F2, F4 y SUBI R2, R2, #4 en la 12ª instrucción) pueden ir contenida en la instrucción 13ª. Con este procesador por tanto se necesitan 18 ciclos de reloj para ejecutar 11 instrucciones en la 1ª iteración. En la 2ª y sucesivas se necesitan 8 ciclos por 9 instrucciones. Por lo tanto el rendimiento es menor que el anterior caso y como se puede ver no se aprovechan todas las capacidades de paralelización de este procesador al quedar muchos huecos (NOPS) en las instrucciones que no se aprovechan para realizar más operaciones. Cómo se podría mejorar el rendimiento? Intentando aumentar el número de operaciones que se ejecutan por instrucción. Para conseguir generar más operaciones y aumentar el paralelismo la mejor manera es desenrollar el bucle un número determinado de veces (contra más mejor). De esta forma se generan más instrucciones en ensamblador que se pueden llegar a meter como operaciones en las instrucciones VLIW. 14