departamento de arquitectura y tecnología de computadores

62
Departamento de Arquitectura y Tecnología de Computadores E.T.S. Ingeniería Informática Práctica 3. Optimización de Código Julio Ortega Lopera. Curso 2008/2009 ARQUITECTURA DE COMPUTADORES I

Upload: nuala

Post on 12-Jan-2016

50 views

Category:

Documents


0 download

DESCRIPTION

ARQUITECTURA DE COMPUTADORES I. Práctica 3. Optimización de Código. Departamento de Arquitectura y Tecnología de Computadores. E.T.S. Ingeniería Informática. Julio Ortega Lopera. Curso 2008/2009. Bibliografía. http://www.intel.com/design/PentiumIII/manuals/ - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Departamento de Arquitectura y Tecnología de Computadores

Departamento de Arquitectura y Tecnología de Computadores

E.T.S. Ingeniería

Informática

Práctica 3. Optimización de Código

Julio Ortega Lopera. Curso 2008/2009

ARQUITECTURA DE COMPUTADORES I

Page 2: Departamento de Arquitectura y Tecnología de Computadores

2Arquitectura de Computadores I. Práctica 3

Bibliografía1. http://www.intel.com/design/PentiumIII/manuals/

2. http://developer.intel.com/design/pentium4/manuals/index.htm

3. GERBER, R.:”The Software Optimization Cookbook. High Performance Recipes for the Intel Architecture”. Intel Press, 2002.

4. GERBER, R.; et al.:”The Software Optimization Cookbook. High Performance Recipes for the IA-32 Platforms”. Intel Press, 2006.

5. FOG, A.:”How to Optimize for the Pentium family of microprocessors”, http://cr.yp.to/2005-590/fog.pdf, 2004.

Page 3: Departamento de Arquitectura y Tecnología de Computadores

3Arquitectura de Computadores I. Práctica 3

Herramientas para realizar la Práctica

Compilador: Intel C++

Herramienta de análisis: VTune

Versiones de evaluación a partir de la página:

developer.intel.com/design/index.htm

(a partir de la opción “Intel Software Evaluation Center”)

Se puede utilizar el compilador de C de Intel para compilar y la herramienta VTune para visualizar los distintos códigos

En esta práctica se puede utilizar el compilador de C que cada uno desee. No obstante el que se ha considerado para plantear los ejercicios a realizar es el GCC para DOS/WINDOWS:

www.delorie.com/djgpp

Page 4: Departamento de Arquitectura y Tecnología de Computadores

4Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 5: Departamento de Arquitectura y Tecnología de Computadores

5Arquitectura de Computadores I. Práctica 3

CUESTIONES GENERALES SOBRE OPTIMIZACIÓN (I)

• Explotando las características de la microarquitectura de un procesador recientemente aparecido, un programador puede aprovechar nuevas propiedades que ofrece dicho procesador antes de que las mismas se hayan incorporado en un compilador o en una biblioteca y estén disponibles en el mercado (con el coste correspondiente):

Ejemplo:

La instrucción CMOV se incorpora al repertorio de instrucciones x86 con el Pentium Pro, en 1995.

El repertorio SSE (Streaming SIMD Extension) aparece con el Pentium III en 1999.

Sin embargo, hasta la versión de 2003 del Visual C++ .NET no se aprovechaba ni la instrucción CMOV, ni las instrucciones SSE para generar código eficiente

Page 6: Departamento de Arquitectura y Tecnología de Computadores

6Arquitectura de Computadores I. Práctica 3

CUESTIONES GENERALES SOBRE OPTIMIZACIÓN (II)

• Usualmente la optimización de una aplicación se realiza al final del proceso, si queda tiempo. Esperar al final para optimizar dificulta el proceso de optimización.

• Es un error escribir la aplicación sin tener en cuenta la arquitectura o arquitecturas en las que se va a ejecutar.

• No es correcto optimizar eliminando propiedades y funciones (features) del código, en el caso de que no se satisfagan las restricciones de tiempo.

• La optimización debe realizarse durante el proceso de desarrollo, utilizando las características de optimización del compilador (no es adecuado desactivar estas opciones para facilitar la depuración del código).

• Cuando se optimiza código es importante analizar donde se encuentran los cuellos de botella. El cuello de botella más estrecho es el que al final determina las prestaciones y es el que debe evitarse en primer lugar.

• Se puede optimizar sin tener que acceder al nivel del lenguaje ensamblador (en algunos casos sí).

Page 7: Departamento de Arquitectura y Tecnología de Computadores

7Arquitectura de Computadores I. Práctica 3

CUESTIONES GENERALES SOBRE OPTIMIZACIÓN (III)

Optimizaciones desde el Lenguaje de Alto Nivel (OHLL)

Optimizaciones desde el Lenguaje Ensamblador (OASM)

Optimizaciones aplicables a

cualquier procesador

(OGP)

Optimizaciones específicas para un procesador

(OEP)

Optimizaciones aplicables a

cualquier procesador

(OGP)

Optimizaciones específicas para un procesador

(OEP)

Una Clasificación de las Optimizaciones

Un Compilador puede ejecutarse utilizando diversas opciones de optimización. Por ejemplo el compilador de c gcc dispone de las opciones –O1, -O2, -O3, -Os que proporcionan códigos con distintas opciones de optimización.

Es posible encontrar una descripción de las distintas alternativas de optimización en:

http://gcc.gnu.org/onlinedocs/

Page 8: Departamento de Arquitectura y Tecnología de Computadores

8Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 9: Departamento de Arquitectura y Tecnología de Computadores

9

IF1 IF2 IF3 ID1 ID2 RAT ROBEmit(RS)

EX RET1RET2

VariosCiclos

Captación Decodificación RenombradoEscritura ROB

Emisión

Retirar(3uops/ciclo)

Arquitectura de Computadores I. Práctica 3

Etapas del Cauce en la microarquitectura P6

Page 10: Departamento de Arquitectura y Tecnología de Computadores

10

3 x 118 Bytes

IF1

IF2

IF3

ID2

RAT

16 Bytes

16 Bytes

16 Bytes

De la cache deInstrucciones

32 Bytes

ID1 D0 D1 D2

6 x 118 Bytes

3 x 118 Bytes

Al ROB

PredicciónEstática de

Saltos

Secuenciador deMicroinstruccion

es

PredicciónDinámica de Saltos

IPSiguiente

Arquitectura de Computadores I. Práctica 3

Captación y Decodificación en la microarq. P6

Page 11: Departamento de Arquitectura y Tecnología de Computadores

11Arquitectura de Computadores I. Práctica 3

Optimización de la Captación en P6 (I)

Los bloques ifetch (como máximo de 16 bytes, no alineados) pasan desde del buffer de instrucciones captadas (32 bytes como máximo, alineados) a los decodificadores.

Los bloques ifetch comienzan en el inicio de una instrucción y tienen 16 bytes, salvo que haya una instrucción de salto para la que se tenga historia (predicción dinámica

Si se produce salto, pueden pasar dos ciclos hasta que se capte la instrucción siguiente (si la instrucción cruza un límite de 16 bytes y hacen falta dos accesos): es beneficioso que haya más instrucciones en el bloque ifetch que puedan pasar a decodificarse mientras se espera la instrucción correspondiente.

Saber la forma en que se van delimitando los ifetch (sobre todo después de un salto) permite determinar qué instrucciones van a ir pasando a decodificarse y se puede mejorar el rendimiento de la decodificación (como se verá)

Hasta que no se ha terminado de decodificar un ifetch no se inicia el siguiente.

Page 12: Departamento de Arquitectura y Tecnología de Computadores

12Arquitectura de Computadores I. Práctica 3

Optimización de la Captación en P6 (II)

En la siguiente tabla se muestran las reglas que permiten conocer el alineamiento del primer bloque ifetch después de un salto.

Número de Grupos de

Decodificación en el bloque ifetch que contiene un

salto

Hay límite de 16 bytes en el bloque

ifetch

Límite de 16 bytes en la primera instrucción

después de un salto

Retardo para empezar la

decodificación

Alineamiento del primer bloque

ifetch después de un salto

1 0 a 16 bytes

1 X 1 a la instrucción

1 X 1 a 16 bytes

1 X X 2 a la instrucción

2 0 a la instrucción

2 X 0 a la instrucción

2 X 0 a 16 bytes

2 X X 1 a la instrucción

3 - 0 a la instrucción

3 - X 0 a la instrucción

3 - X 0 a la instrucción

3 - X X 0 a la instrucción

Page 13: Departamento de Arquitectura y Tecnología de Computadores

13

Primer IFETCH: 1000h –1010h (sin incluir) (2 ciclos en decodificar)

Segundo IFETCH: 1007h –1017h (sin incluir) (1 ciclo en decodificar)

Tercer IFETCH: 1017h – 1022h (inclusive) (3 ciclos en decodificar)

La primera iteración del bucle LL necesita 5 ciclos para decodificarse

El primer bloque ifetch después del salto empezará en la instrucción LL ya que el último bloque ifetch tiene una alineación de 16 bytes y tres grupos de decodific. en 1020h (desde 1005h a 1015h)

El siguiente bloque ifetch empieza en 1011h y termina antes de la 1021h y el último bloque empieza en 1021h hasta el final (y no incluye límites de 16 bytes)

Se necesitan 7 ciclos para decodificar la segunda iteración y el bloque ifetch de la siguiente iteración empieza en un límite de 16 bytes que incluya a la dirección de salto (1000h)

Las iteraciones impares necesitan 5 ciclos y las pares 7 ciclos para decodificarse

Optimización de la Captación en P6 (III)

Ejemplo

Page 14: Departamento de Arquitectura y Tecnología de Computadores

14Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 15: Departamento de Arquitectura y Tecnología de Computadores

15Arquitectura de Computadores I. Práctica 3

Optimización de la Decodificación (I)

Los decodificadores pueden manejar tres instrucciones por ciclo pero sólo si se reúnen una serie de condiciones que se resumen a continuación:

La primera instrucción, decodificada en D0 no puede generar más de 4 uops en un sólo ciclo de reloj, y la segunda y tercera instrucción no deben generar más de 1 uop cada una.

La segunda y tercera instrucción no debe tener más de 8 bytes cada una.

Las instrucciones deben estar contenidas en el mismo bloque de ifetch de 16 bytes.movl _MEM1, %ebx 1 uop (D0)

incl %ebx 1 uop (D1)

addl _MEM2, %eax 2 uops (D0)

addl %eax, _MEM3 4 uops (D0)

addl %eax, _MEM3 4 uops (D0)

movl _MEM1, %ebx 1 uop (D1)

incl %ebx 1 uop (D2)

addl _MEM2, %eax 2 uops (D0)

Se gana un ciclo en la decodificación (3 2) Microarquitectura P6

Page 16: Departamento de Arquitectura y Tecnología de Computadores

16Arquitectura de Computadores I. Práctica 3

Optimización de la Decodificación (II)

Los prefijos que tienen ciertas instrucciones también pueden ocasionar pérdidas de ciclos en los decodificadores.

Prefijo de tamaño de operando cuando se tiene un operando de 16 bits en un entorno de 32 o viceversa (Operand-size override prefix, 66h).

Prefijo de tamaño de dirección (Address-size override prefix, 67h).

Un prefijo produce un ciclo de penalización (si se utiliza más de un prefijo, habría una penalización de un ciclo por prefijo) si:

•El prefijo de tamaño de operando se utiliza con un operando inmediato

•El prefijo de ajuste de dirección se utiliza con una dirección que incluye un offset.

movw $0x77, _mem

movl %edx,_mem(%ax)

movl $0x77,%eax

movw %ax,_mem

addw _mem,%ax

movl %edx,(%ax)

Prefijo 66h en el modo de 32 bits (almacena un dato de 16 bits en memoria)

Se ahorra un ciclo

Prefijo 67h (almacena un dato en memoria utilizando un offset) Microarquitectura P6

Page 17: Departamento de Arquitectura y Tecnología de Computadores

17Arquitectura de Computadores I. Práctica 3

Optimización de la Decodificación (III)

En la microarquitectura NetBurst el decodificador puede generar de 1 a 4 uops por instrucción y ciclo.

Las instrucciones que necesitan más de 4 uops se envían a una memoria ROM de microcódigo. En este caso, la instrucción puede tardar más de un ciclo en decodificarse.

Las instrucciones que tienen más de un prefijo necesitan un ciclo de decodificación por prefijo.

El tiempo de decodificación no es importante en el caso de bucles que quepan en la cache de traza.

Si las uoperaciones correspondientes a una instrucción no están en la cache de traza, éstas pasan a las etapas de ejecución desde el decodificador y, en este caso, la velocidad de decodificación sí es relevante (las instrucciones pasan al decodificador desde la cache L2).

Las trazas de código que tardan en decodificarse más que en ejecutarse son las que suelen incluirse en la cache de traza (de forma automática).

Microarquitectura NetBurst

Page 18: Departamento de Arquitectura y Tecnología de Computadores

18Arquitectura de Computadores I. Práctica 3

Cache de Traza en NetBurst (I)

En el Pentium 4 las instrucciones se introducen en una cache de traza tras haber sido decodificadas en uoperaciones (en lugar de almacenarse las instrucciones en una cache L1 para instrucciones, en la cache de traza se almacenan las trazas de las uoperaciones consecutivas correspondientes).

Existe una cache L2 para código y datos de 256 KB como mínimo, con un bus de acceso de 256 bits.

La cache de traza está organizada en 2048 líneas con espacio para 6 uops cada una y con correspondencia asociativa por conjuntos de 4 vías.

En el espacio disponible para cada uop hay 16 bits de datos: si una uop necesita más bits para los datos, ocupa más espacio (espacio de varias uops). Hay que tener en cuenta que, ninguna uop depende de más de dos operandos de entrada.

Por ejemplo:

CMOVcc puede tener más de dos operandos y se divide en dos uops

MOV [ESI+EDI], AX también da lugar a dos uops

MOV EAX,[MEM1] necesita dos espacios de la cache (la dirección puede tener más de 16 bits)

Page 19: Departamento de Arquitectura y Tecnología de Computadores

19Arquitectura de Computadores I. Práctica 3

Cache de Traza en NetBurst (II)

Las uops pueden extraerse de la cache a una velocidad que corresponde a 1 línea de cache cada dos ciclos: esto supone unas 3 uops por ciclo (aproximadamente).

Es conveniente que ninguna uop que ocupe dos espacios cruce los límites de 6 espacios que correspoden al final/comienzo de una línea. Por ejemplo, se podrían reordenar de forma que haya un número par (considerando al 0 como número par) de uops que ocupan un espacio entre cada dos instrucciones de doble espacio.MOV EAX, [MEM1] (1 uop, 2 esp.)

ADD EAX, 1 (1 uop, 1 esp.)MOV EBX,[MEM2] (1 uop, 2 esp.)MOV [MEM3],EAX (1 uop, 2 esp.)ADD EBX,1 (1 uop, 1 esp.)MOV EAX, [MEM1] (1 uop, 2 esp.)MOV EBX,[MEM2] (1 uop, 2 esp.)ADD EAX, 1 (1 uop, 1 esp.) ADD EBX,1 (1 uop, 1 esp.)MOV [MEM3],EAX (1 uop, 2 esp.)

Page 20: Departamento de Arquitectura y Tecnología de Computadores

21Arquitectura de Computadores I. Práctica 3

Cache de Traza en NetBurst (IV)

Líneas generales para mejorar las prestaciones de la cache de traza:

1. Utilizar instrucciones que generen pocas uops

2. Utilizar datos inmediatos entre -215 y 215, si es posible

3. Evitar direccionamientos directos con direcciones de 32 bits

4. Evitar un número impar de uops que necesitan un solo espacio entre uops de dos espacios

5. Intentar sustituir las instrucciones de salto condicional por instrucciones de movimiento condicional (si eso no implica costos excesivos por otro lado)

Page 21: Departamento de Arquitectura y Tecnología de Computadores

22Arquitectura de Computadores I. Práctica 3

Optimización del Renombrado (I)

Para la optimización de la etapa de renombrado en el RAT, lo ideal es que, en un ciclo, no se introduzcan en el RAT grupos de uoperaciones que lean más de dos registros del banco de registros.

Se pueden seguir ciertas recomendaciones:

• Mantener las uoperaciones que leen el mismo registro lo más cerca posible para que sea más probable que entren a la vez en el RAT.

• Mantener las uoperaciones que leen de registros diferentes lo más lejos posible para que no entren a la vez en el RAT.

• Provocar renombrados de registros para evitar los ciclos perdidos en el acceso a los registros (si no se introducen muchas uoperaciones).

Page 22: Departamento de Arquitectura y Tecnología de Computadores

23

MOV EAX, EBX

SUB ECX, EAX

INC EBX

MOV [EAX], EDX

ADD ESI, EBX

ADD ESI, ECX

Todas las instrucciones generan una uop.

Las tres primeras pasan al RAT. Utilizan tres registros, pero como EAX se escribe antes, se le asignará una entrada en el ROB (se renombra) y se lee desde allí. Sólo se leen EBX y ECX.

En la segunda tanda de tres uops se necesitan más de dos registros, pero como se ha escrito antes sobre EBX, y ECX sólo ESI y EDX deben leerse desde el fichero de registros

Ninguna uop usa más de dos operandos (todas las instrucciones que usan más de dos registros dan lugar a dos o más uops)

Lo ideal es que no pasen a la vez por el RAT uops que lean más de dos registros del fichero de registros: es difícil predecir qué uops pasan por el RAT en cada ciclo (un salto mal predicho descarta las instrucciones de la cola entre la unidad de decodificación y el ROB y habría que tener en cuenta el orden en que se generan las uops).

Si la lectura de un operando se hace desde el ROB (en lugar de utilizarse el Banco de Registros) no existen limitaciones para el número de lecturas (la limitación en el número de lecturas viene del número de puertos de lectura del banco de registros).

Arquitectura de Computadores I. Práctica 3

Optimización del Renombrado (II)

Page 23: Departamento de Arquitectura y Tecnología de Computadores

24Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 24: Departamento de Arquitectura y Tecnología de Computadores

25Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: Unidades de ejecución

Se podrían realizar cuatro sumas por ciclo pero hay que tener en cuenta que la cache de traza sólo emite tres uops por ciclo: esta velocidad de ejecución se puede conseguir si las instrucciones han estado en cola durante un cierto periodo de tiempo previo.

Si todos los puertos estuviesen generando resultados a su máxima velocidad se podrían terminar 6 uops por ciclo (pero hay que tener en cuenta que las instrucciones se retiran a un ritmo de 3 uops por ciclo, como en la microarquitectura P6.

La mayoría de las subunidades están segmentadas (la fp-div no está segmentada, y puede tardar entre 23 y 43 ciclos de reloj)

Puerto Unidad Operaciones (Subunidades) Velocidad (resultados/ciclo)

p0 ALU0 add, sub, mov, lógicas, saltos, store entero 2

p0 MOV mov y store fp, fxch 1

p1 ALU1 add, sub, mov 2

p1 INT misc 1

p1 FP fp add, mul, div, misc 1/2

p1 MMX mmx alu, shift, misc 1/2

p2 LOAD Todas las cargas 1

p3 STORE almacenamientos con direcciones 1

Microarquitectura NetBurst

Page 25: Departamento de Arquitectura y Tecnología de Computadores

26Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: Registros parciales

Se puede producir un 'atasco' en el cauce debido al uso de un registro parcial si se lee un registro de mayor tamaño después de una escritura en un registro parcial

Para evitar ese tipo de atascos se tendría que borrar el registro mayor con XOR o SUB antes de escribir sobre el registro parcial. Borrar el registro mayor con MOV no evita el atasco.

mov eax, 0

mov ax, mem16

add ecx, eax

xor eax, eax

mov ax, mem16

add ecx, eaxmov ah,cl mov eax,ecx

mov al,dl shl eax,8

mov mem, eax and edx,0xff

Sin atasco

Con atasco

Page 26: Departamento de Arquitectura y Tecnología de Computadores

27Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: Bit de Estado

Una escritura en alguno (no en todos) de los flags de estado del registro EFLAGS precede a una lectura tanto de los flags modificados como no modificados (no hay problema si se leen sólo los modificados o sólo los no modificados).

sahf // almacena el registro ah en el registro de flags excepto OFjg label // jg lee el flag OF junto con los SF y ZF

sahf // no hay atascoinstrucciones que no leen los flagsjg label

Instrucciones como INC y DEC que no actualizan todos los flags de estado (no actualizan el flag de acarreo, CF) pueden provocar atascos:

En estos casos es mejor utilizar ADD o SUB (Ojo en el control de bucles!!)

Page 27: Departamento de Arquitectura y Tecnología de Computadores

28Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: Desenrollado de Bucles

Utilizar el desenrollado de bucles para romper secuencias de instrucciones dependientes intercalando otras instrucciones.

float dot-product(float *a, float *b){int i; float tmp=0.0; for (i=0; i<ARR; i++) {

tmp += a[i]*b[i];}ret tmp;}

float dot-product(float *a, float *b){int i; float tmp0=0.0; float tmp1=0.0; float tmp2=0.0; float tmp3=0.0; for (i=0; i<ARR; i+=4) {

tmp0 += a[i]*b[i];tmp1 += a[i+1]*b[i+1];tmp2 += a[i+2]*b[i+2];tmp3 += a[i+3]*b[i+3];

}ret tmp0+tmp1+tmp2+tmp3;}

El desenrollado:

Reduce el número de saltos

Aumenta la oportunidad de encontrar instrucciones independientes

Facilita la posibilidad de insertar instrucciones para ocultar las latencias.

La contrapartida es que aumenta el tamaño de los códigos.

for (i=0;i<100;i++) if ((i%2) = = 0) a[i]=x; else a[i]=y;

for (i=0;i<100;i+=2) {

a[i]=x;

a[i+1]=y; } Ejemplo

Page 28: Departamento de Arquitectura y Tecnología de Computadores

29Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: Unidades Funcionales de la Microarquitectura

Es importante tener en cuenta las unidades funcionales de que dispone la microarquitectura para utilizar las instrucciones de la forma más eficaz. Así:

- La división es una operación muy costosa y por lo tanto habría que evitarla (por ejemplo, utilizando desplazamientos) o reducir su número.

for (i=0;i<100;i++) a[i]=a[i]/y;

temp=1/y;

for (i=0;i<100;i++) a[i]=a[i]*temp;

- A veces es más rápido utilizar desplazamientos y sumas para realizar una multiplicación por una constante entera que utilizar la instrucción IMUL.

imul eax,10 lea ecx,[eax+eax]

lea eax,[ecx+eax*8]

Page 29: Departamento de Arquitectura y Tecnología de Computadores

30Arquitectura de Computadores I. Práctica 3

Optimización de la Ejecución: No utilizar código ambiguo

No utilizar código ambiguo ya que si los compiladores no pueden resolver los punteros, tampoco pueden realizar ciertas optimizaciones, asignar variables durante la compilación, realizar cargas de memoria mientras que un almacenamiento está en marcha.

Para evitar esto: Utilizar variables locales en lugar de punteros, utilizar variables globales si no se pueden utilizar las locales, y poner las instrucciones de almacenamiento después o bastante antes de las de carga de memoria.

No obstante, si no se utilizan punteros el código es más dependiente de la máquina, y a veces las ventajas de no utilizarlos no compensa.

int j; int j;

void mars (int *v) { void mars(int v) {

j=7.0; j=7.0;

*v=15; v=15;

j/=7; j/=7;

........ .........

} }

En el código no optimizado, el compilador no puede asumir que *v no apunt a j. Sin embargo en el código optimizado, el compilador puede hacer directamente j=1.0.

Page 30: Departamento de Arquitectura y Tecnología de Computadores

32Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 31: Departamento de Arquitectura y Tecnología de Computadores

33Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Alineación de Datos (II)

En el Pentium Pro, Pentium II y Pentium III, el acceso a los datos no alineados supone un costo adicional de entre 6 y 12 ciclos cuando se cruza el límite de la línea de cache. Los operandos menores de 16 bytes que no crucen una línea de cache no dan lugar a penalización.

Es posible controlar, desde un programa escrito en un lenguaje de alto nivel como C, la alineación de los datos que utiliza dicho programa y con ello evitar la penalización que pueda producirse por una falta de alineación.

BOUND=32 si se quiere que esté alineado con líneas de cache de 32 bytes

struct s *p, *new_p;

p= (struct s*)

malloc(sizeof(struct s) + BOUND –1);

new_p = (struct s*)

(((int) p+BOUND-1)& ~(BOUND-1);

p

32 bytes

new_p

struct s *p, *new_p;

p= (struct s*)

malloc(sizeof(struct s) + BOUND –1);

new_p = (struct s*)

(((int) p+BOUND-1)& ~(BOUND-1);

p

32 bytes

new_pnew_p

N : Tamaño del vectorBOUND : 0x20 = 00000000000000000000000000100000BOUND – 1 : 0x1F = 00000000000000000000000000011111~(BOUND-1): 0xFFE0 = 11111111111111111111111111100000

Page 32: Departamento de Arquitectura y Tecnología de Computadores

34Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Alineación de Datos (II)

Cuando el código accede a un array de forma no secuencial es conveniente ajustar y alinearla estructura de forma que ocupen el mínimo número de líneas de cache.

Ejemplo:

En el código del ejemplo se accede a la primera y a la última estructura de un array de 5 estructuras de 28 bytes. Cuando se accede a una estructura se producen dos faltas de acceso a cache en lugar de una (en el caso de estas dos estructuras, tal y como se consideran situadas en el ejemplo)

Código original Código Optimizadostruct { struct{

int a[7]; int a[7];} s[5]; int pad;.......... } s[5];for (i=0;i<7;i++)s[1].val[i]+=s[5].val[i]; p=(struct s*) malloc (sizeof (struct s)*5+32); new_p=(p+31)&(-31);

Se añaden variables ficticias para llenar 32 bytes y se alinean las estructuras para que empiecen en una línea de cache.

Page 33: Departamento de Arquitectura y Tecnología de Computadores

35Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Colisiones en Cache

Puesto que la cache es asociativa por conjuntos de cuatro vías en el Pentium II y Pentium III y las líneas son de 32 bytes, los bits 5 a 11 de la dirección física de memoria (0 es el bit menos significativo) indican el conjunto en el que se introduce la línea correspondiente.

Para conocer si dos líneas se almacenan en cache en el mismo conjunto, se toman dos direcciones, una de cada línea y se hacen igual a 0 los 5 bits menos significativos de ambas. Si la diferencia de las nuevas direcciones es múltiplo de 4096, las líneas a las que pertenecen esas direcciones van al mismo conjunto.

El ejemplo siguiente ilustra un procedimiento para asegurar que dos zonas de datos se asignan a distintos conjuntos (y no colisionen en cache): int *tempA, *tempB;

.....................pA= (int *) malloc (sizeof(int)*N + 31); tempA = (int *)(((int)pA+31)&~(31));tempB = (int *)((((int)pA+31)&~(31))+4096+32);

Los punteros tempA y tempB están apuntando a zonas de memoria que empiezan en posiciones que son múltiplos de 32 y que no se asignarían al mismo conjunto de cache.

Page 34: Departamento de Arquitectura y Tecnología de Computadores

36Arquitectura de Computadores I. Práctica 3

La forma en que se declaren los arrays determina la forma en que se almacenan en memoria. Interesa declararlos según se vaya a realizar el acceso.

Ejemplos: Formas óptimas de declaración de variables según el tipo de acceso a los datos

struct { struct {int a[500]; int a;int b[500]; int b;

} s; } s[500];....... ............for (i=0; i<500; i++) for (i=0;i<500;i+

+)s.a[i]=2*s.a[i]; {

....... s[i].a+=5;for (i=0;i<500;i++) s[i].b+=3;

s.b[i]=3*s.b[i]; }

Optimización del Acceso a Memoria y Cache: Almacenamiento y Referencia a los datos (I)

Page 35: Departamento de Arquitectura y Tecnología de Computadores

37Arquitectura de Computadores I. Práctica 3

Intercambiar los bucles para cambiar la forma de acceder a los datos según los almacena el compilador, y para aprovechar la localidad

Ejemplo:

Código Original

for (j=0; j<4; j++)

for (i=0;i<4;i++)

a[i][j]=2*a[i][j];

Código Optimizado para C (se almacena la matriz por filas)

for (i=0; i<4; i++)

for (j=0;j<4;j++)

a[i][j]=2*a[i][j];

Optimización del Acceso a Memoria y Cache: Almacenamiento y Referencia a los datos (II)

Page 36: Departamento de Arquitectura y Tecnología de Computadores

38Arquitectura de Computadores I. Práctica 3

Los 'atascos' (stalls) por acceso a la memoria (load adelanta a store, especulativo) se producen cuando:

• Hay una carga (load) 'larga' que sigue a un almacenamiento (store) 'pequeño' alineados en la misma dirección o en rangos de direcciones solapadas.

mov word ptr [ebp],0x10mov ecx, dword ptr [ebp]

• Una carga (load) 'pequeña' sigue a un almacenamiento (store) 'largo' en direcciones diferentes aunque solapadas (si están alineadas en la misma dirección no hay problemas).

mov dword ptr [ebp-1], eaxmov ecx,word ptr [ebp]

• Datos del mismo tamaño se almacenan y luego se cargan desde direcciones solapadas que no están alineadas.

mov dword ptr [ebp-1], eaxmov eax, dword ptr [ebp]

Para evitarlos: Utilizar datos del mismo tamaño y direcciones alineadas y poner los loads tan lejos como sea posible de los stores a la misma área de memoria

Optimización del Acceso a Memoria y Cache: Mejora del rendimiento del acceso especulativo

Page 37: Departamento de Arquitectura y Tecnología de Computadores

39

for (i=0;i<N;i=i+1) for (j=0;j<N;j=j+1) { r=0; for (k=0; k<N;k=k+1) { r= r + y[i][k]*z[k][j]; } ; x[i][j] = r; };

for (jj=0;jj<N;jj=jj+B)for (kk=0;kk<N;kk=kk+B)for (i=0;i<N;i=i+1) for (j=jj;j<min(jj+B-1,N);j=j+1) { r=0; for (k=kk; k<min(kk+B-1,N);k=k+1) { r= r + y[i][k]*z[k][j]; } ; x[i][j] = x[i][j] + r; };

Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Blocking

Page 38: Departamento de Arquitectura y Tecnología de Computadores

40Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Pre-captación (I)

Pentium 4:

Cache L1 de datos con líneas de 64 bytes (128 líneas * 64 bytes/línea = 8 KBytes) , asociativa por conjuntos de 4 vías.

Cache L2 unificada, con líneas de 128 bytes (2K líneas * 128 bytes = 256 Kbytes), asociativa por conjuntos de 8 vías.

El procesador, mediante las correspondientes instrucciones de prefetch, carga zonas de memoria en cache antes de que se soliciten (cuando hay ancho de banda disponible).

Hay cuatro tipos de instrucciones de prefetch:

Instrucción ensamb.Segundo parámetro en la función C++

_mm_prefetchDescripción

prefetchnta _MM_HINT_NTAPrefetch en buffer no temporal (dato para una lectura)

prefetcht0 _MM_HINT_T0 Prefetch en todas las caches útiles

prefetcht1 _MM_HINT_T1 Prefetch en L2 y L3 pero no en L1

prefetcht2 _MM_HINT_T2 Prefetch sólo en L3

Page 39: Departamento de Arquitectura y Tecnología de Computadores

41Arquitectura de Computadores I. Práctica 3

Optimización del Acceso a Memoria y Cache: Pre-captación (II)

El aspecto crucial al realizar precaptación es la anticipación con la que se pre-captan los datos. En muchos casos es necesario aplicar una estrategia de prueba y error.

Además, la anticipación óptima puede cambiar según las características del computador (menos portabilidad en el código).

Una instrucción de prefetch carga una línea entera de cache (en el Pentium 4 sólo sería necesario captar 1 byte de cada 64 byte)

For (i=0; i<1000; i++)

{

x=function(matriz[i]);

_mm_prefetch(matriz[i+16],_MM_HINT_T0);

}- En el ejemplo se precapta el dato necesario para la iteración situada a 16

iteraciones (en el futuro)

- En un prefetch no se generan faltas de memoria (es seguro precaptar más allá de los límites del array

Page 40: Departamento de Arquitectura y Tecnología de Computadores

44Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 41: Departamento de Arquitectura y Tecnología de Computadores

45Arquitectura de Computadores I. Práctica 3

OPTIMIZACIÓN DE SALTOS

if (t1==0 && t2==0 && t3==0)

if ((t1 | t2 | t3)==0)

Cada una de las condiciones separadas por && se evalúa mediante una instrucción de salto distinta.

Si las variables pueden ser 1 ó 0 con la misma probabilidad, la posibilidad de predecir esas instrucciones de salto no es muy elevada. Si se utiliza un único salto, la probabilidad de 1 es de 0.125 y la de 0 de 0.875 y la posibilidad de hacer una buena predicción aumenta.

// if ((t1 | t2 | t3)==0) { t4=1};//t1 -> edi t2 -> ebx t3 -> ebp t4 -> eaxmov ecx, 1or edi, ebxor edi, ebpcmove eax, ecx

Mediante la instrucción de movimiento condicional se pueden evitar los daltos

Page 42: Departamento de Arquitectura y Tecnología de Computadores

46Arquitectura de Computadores I. Práctica 3

OPTIMIZACIÓN DE SALTOS: MEJORA DE LA PREDICCIÓN (I)

T

T

T

T

NT

NT

NT

NT

11 B

10 B

01 NB

00 NB

T

T

T

T

NT

NT

NT

NT

11 B

10 B

01 NB

00 NB

....

....

0 0 0 1

0000 0001 1111

Bits de Historia: Registro de Desplazamiento

Predicción Dinámica (36 bits)

Dada una secuencia de saltos/no saltos para una instrucción de salto, si toda subsecuencia de cuatro bits (indicando cada bit si se produce salto o no) va seguida del mismo bit, la secuencia es predecible (tras el correspondiente número de ciclos de aprendizaje)

Cuando no hay historia para una instrucción de salto, esto es, la primera vez que se ejecuta, se utiliza el siguiente procedimiento de predicción estática:

• Si la dirección de salto no es relativa al contador de programa IP: Predice 'Saltar' si el salto es un 'return', y 'No Saltar' en caso

contrario.

• Si la dirección de salto es relativa a IP: Predice 'Saltar' si el salto es hacia atrás (situación análoga a los bucles), y 'No Saltar' si el salto es hacia delante.

Secuencia predecible: 1000100010001000Secuencia no predecible:

000001000001

Microarquitectura P6

Page 43: Departamento de Arquitectura y Tecnología de Computadores

47Arquitectura de Computadores I. Práctica 3

OPTIMIZACIÓN DE SALTOS: MEJORA DE LA PREDICCIÓN (II)

• En algunos casos, los bucles se pueden organizar para que den lugar a secuencias predecibles por el esquema de predicción dinámica:

Si un bucle se ejecuta 20 veces no se predice correctamente la última iteración. Para evitar esto se puede utilizar dos bucles anidados de 4 y 5 iteraciones respectivamente, o desenrollar el bucle por cuatro, para que sólo haya 5 iteraciones.

• Si un salto que no tiene ninguna historia almacenada en el BTB se utiliza la predicción estática, que predice los saltos hacia delante como no tomados. Se pueden mejorar las prestaciones del procedimiento si se situa el código más frecuente después del salto condicional hacia delante.

Código Ensamblador Original

comp a, 5je L1Código Infrecuentejmp L2

L1:Código Frecuente

L2:

Código Ensamblador Mejorado

comp a, 5jne L1Código Frecuentejmp L2

L1:Código Infrecuente

L2:

Page 44: Departamento de Arquitectura y Tecnología de Computadores

49Arquitectura de Computadores I. Práctica 3

Se puede reducir el número de saltos de un programa reorganizando las alternativas en las sentencias switch, en el caso de que alguna opción se ejecute mucho más que las otras (más del 50% de las veces, por ejemplo).

Ciertos compiladores que utilizan información de perfiles de ejecución del programa son capaces de realizar esta reorganización (Se recomienda utilizarla si la sentencia switch se implementa como una búsqueda binaria en lugar de una tabla de salto).

OPTIMIZACIÓN DE SALTOS: REDUCCIÓN DE SALTOS (I)

Código originalswitch (i){ case 16: Bloque16 break;case 22: Bloque22 break;case 33: Bloque33 break;}

Código Optimizadoif (i==33) { Bloque33 }elseswitch (i){ case 16: Bloque16 break; case 22: Bloque22 break;}

Page 45: Departamento de Arquitectura y Tecnología de Computadores

50Arquitectura de Computadores I. Práctica 3

OPTIMIZACIÓN DE SALTOS: REDUCCIÓN DE SALTOS (II)

CMOVcc hace la transferencia de información si se cumple la condición indicada en cc

test ecx,ecx test ecx,ecxjne 1h cmoveq eax, ebxmov eax,ebx1h:

FCMOVcc es similar a CMOVcc pero utiliza operandos en coma flotante

Page 46: Departamento de Arquitectura y Tecnología de Computadores

51Arquitectura de Computadores I. Práctica 3

OPTIMIZACIÓN DE SALTOS: REDUCCIÓN DE SALTOS (III)

La instrucción SETcc es otro ejemplo de instrucción con predicado que puede permitir reducir el número de instrucciones de salto.

ebx= (A<B) ? C1 : C2; [ Si (A<B) es cierto EBX se carga con C1 y si no con C2 ]

Código Originalcmp A,Bjge L30mov ebx,C1jmp L31

L30: mov ebx,C2 L31:Código Optimizado Explicación:xor ebx,ebx Si A>=B, setge hace BL=1; DEC hace EBX=0; (EBX and C1-C2)=0;

EBX + C2 = C2cmp A,Bsetge bl Si A<B, setge hace BL=0; DEC hace EBX=0xFFFFFFFF;

(EBX and C1-C2)= C1-C2; dec ebx EBX+C2=C1and ebx, (C1-C2)add ebx, C2

Page 47: Departamento de Arquitectura y Tecnología de Computadores

52Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 48: Departamento de Arquitectura y Tecnología de Computadores

53

OPTIMIZACIÓN DE BUCLES (I)

En un programa, a menudo, la mayor parte del tiempo se pasa en uno de los bucles del mismo. La forma más directa de mejorar la velocidad consiste en optimizar cuidadosamente el bucle que más tiempo consuma utilizando el lenguaje ensamblador.

En los ejemplos que se considerarán a continuación, se asume que los datos están en la cache de nivel 1. Si la velocidad está limitada por los fallos de cache habría que concentrarse primero en distribuir los datos para disminuir los fallos.

Como ejemplo se tomará un procedimiento sencillo en C:

void Cambiosigno (int *A, int *B, int N) {int i;for (i=0; i<N; i++) B[i]= -A[i] ;}

Para optimizar el código abría que tener en cuenta:1. La alineación de los bloques de captación (ifetch) y la decodificación2. Los atascos (stalls) en la lectura de registros y las características de ejecución de uops 3. El desenrollado de bucles

Arquitectura de Computadores I. Práctica 3

Page 49: Departamento de Arquitectura y Tecnología de Computadores

54

OPTIMIZACIÓN DE BUCLES (II)

Este código puede mejorarse más, tratando de evitar las instrucciones que generan muchas uops, como por ejemplo LOOP, LODS, y STOSD.

A continuación se considerarán distintas mejoras del código (zona representada dentro del cuadro de línea discontinua) teniendo en cuenta la microarquitectura del procesador y utilizando el desenrollado de bucles.

.file "cambiosign.c"gcc2_compiled.:___gnu_compiled_c:.text

.p2align 2.globl _changesign_changesign: pushl %ebp movl %esp,%ebp subl $16,%esp pushl %esi pushl %edi

movl 16(%ebp),%ecx jecxz L2 movl 8(%ebp),%esi movl 12(%ebp),%edi .p2align 4,,7 cldL1: lodsl negl %eax stosl loop L1L2: leal -24(%ebp),%esp popl %edi popl %esi movl %ebp,%esp popl %ebp ret

Page 50: Departamento de Arquitectura y Tecnología de Computadores

55

OPTIMIZACIÓN DE BUCLES (III)

A continuación se mostrarán algunas mejoras de este código considerando distintos aspectos de la microarquitectura del procesador:

Decodificación de instrucciones. En la versión mejorada del programa ensamblador hay una instrucción que genera 2 uops (MOV [EDI] ,EAX) y debe ir al decodificador D0. En cada iteración del bucle existen tres grupos de decodificación (se podrían decodificar en tres ciclos).

Límites de los bloques de 16 bytes de instrucciones que se captan. Una forma de mejorar la velocidad es conseguir que las instrucciones del bucle estén alineadas en el menor número posible de estos bloques

Atascos producidos por las lecturas de registros (posibles riesgos de tipo RAW)

Análisis de las uops que van a cada puerto de ejecución. Hay que evitar las colisiones en la medida de lo posible.

Retirada de instrucciones del ROB. En cada ciclo se pueden retirar como mucho 3 uops.

Arquitectura de Computadores I. Práctica 3

Page 51: Departamento de Arquitectura y Tecnología de Computadores

56

OPTIMIZACIÓN DE BUCLES (IV)

pushl %esipushl %edi

movl 16(%ebp),%ecxjecxz L2movl 8(%ebp),%esimovl 12(%ebp),%edi.p2align 4,,7 ; alineado a 16 bytes

L1:movl (%esi),%eax ; 2 p2rESIwEAX addl $4,%esi ; 3 p01rwESIwFnegl %eax ; 2 p01rwEAXwFmovl %eax,(%edi) ; 2 p4rEAX, p3rEDIaddl $4,%edi ; 3 p01rwEDIwFdecl %ecx ; 1 p01rwECXwF

jnz L1 ; 2 p1rFL2:

leal -24(%ebp),%esppopl %edipopl %esi

Hay tres grupos de decodificación en el bucle (3 ciclos de reloj)

Puesto que el bucle entero tiene 15 bytes, se puede introducir completamente en un bloque ifetch de 16 bytes si se alinea el comienzo del bucle a 16 bytes.

No hay atascos de registros (renombrado): cuando se hace una lectura de registro, la escritura en el mismo se ha hecho antes.

En cuanto a la ejecución, para los puertos p0 o p1, hay 4 uops; para p1, 1 uop; y 1 uop para cada unos de los puertos p2, p3, y p4. Suponiendo una distribución óptima de las operaciones esto supone unos 3 ciclos.

El número de ciclos necesario para retirar las uops es igual al menor entero mayor o igual al número de uops dividido por tres.

Cada iteración del Bucle se podría terminar en tres ciclos de reloj.

Page 52: Departamento de Arquitectura y Tecnología de Computadores

57

OPTIMIZACIÓN DE BUCLES (V)

pushl %esi pushl %edi

movl 8(%ebp),%esi movl 12(%ebp),%edi movl 16(%ebp),%ecx leal (%esi,%ecx,4),%esi leal (%edi,%ecx,4),%ebx negl %ecx jz L2 .p2align 4,,7L1: movl (%esi,%ecx,4),%eax ; 3 p2rESIrECXwEAX negl %eax ; 2 p01rwEAXwF movl %eax,(%edi,%ecx,4) ; 3 p4rEAX, p3rEDIrECX incl %ecx ; 1 p01rECXwF jnz L1 ; 2 p1rFL2: leal -24(%ebp),%esp popl %edi popl %esi

Se han reducido 6 uops utilizando el mismo registro como contador e índice. Los punteros base apuntan al final de los arrays para que se pueda contar descendentemente.

Hay dos grupos de decodificación: se puede hacer en 2 ciclos

Alineando el bucle en los bloques de 16, como el número de bytes es 11, sólo se necesitan 2 ciclos para captar las instrucciones.

Hay 2 uops para p0 o p1, y 1 uop para p1, p2, p3, y p4. Se puede ejecutar en unos2 ciclos

Se pueden retirar las uops en dos ciclos.

Se podría terminar cada iteración del Bucle en dos ciclos

Arquitectura de Computadores I. Práctica 3

Page 53: Departamento de Arquitectura y Tecnología de Computadores

58

OPTIMIZACIÓN DE BUCLES (VI). DESENROLLADO

El desenrollado de bucles permite reducir el overhead debido a los saltos y el número de saltos.

Por ejemplo, en el código de la página Optimización de Bucles (IV) el overhead de cada bucle es de 4 uops (actualizar punteros, contador e instrucción de salto) frente a 4 uops para hacer los cálculos en si.

Si se hiciera un desenrollado como indica el código que se muestra (dividiendo el número de iteraciones por la mitad) se pasaría de un 50% de overhead a un 33%.

Existe un código que puede tener un mejor comportamiento que éste en cuanto a que puede reducir el número de ciclos de decodificación y el número de ciclos necesario para retirar las instrucciones del ROB

.p2align 4,,7

L1:

movl (%esi),%eax ;2 p2rESIwEAX

negl %eax ;2 p01rwEAXwF

movl %eax,(%edi) ;2 p4rEAX,p3rEDI

movl 4(%esi),%eax;3 p2rESIwEAX

negl %eax ;2 p01rwEAXwF

movl %eax,4(%edi);3 p4rEAX,p3rEDI

addl $8,%esi ;3 p01rwESIwF

addl $8,%edi ;3 p01rwEDIwF

decl %ecx ;1 p01rwECXwF

jnz L1 ;2 p1rF

Arquitectura de Computadores I. Práctica 3

Page 54: Departamento de Arquitectura y Tecnología de Computadores

59

OPTIMIZACIÓN DE BUCLES (VII). DESENROLLADO

Teniendo en cuenta la distribución de instrucciones, el primer grupo de 16 bytes incluiría hasta la instrucción negl %ebx, y se podrían decodificar en dos ciclos. El resto de instrucciones del bucle se podrían decodificar en otros dos ciclos.

Este código se puede decodificar en 4 ciclos en lugar de los 5 que necesitaba el anterior.

Este código también permite mejorar el rendimiento del ROB.

El coste ha sido la necesidad de introducir un registro extra (el registro EBX)

.p2align 4,,7

L1:

movl (%esi),%eax ;2 p2rESIwEAX

movl 4(%esi),%ebx;3 p2rESIwEBX

negl %eax ;2 p01rwEAXwF

movl %eax,(%edi) ;2 p4rEAX,p3rEDI

addl $8,%esi ;3 p01rwESIwF

negl %ebx ;2 p01rwEBXwF

movl %ebx,4(%edi);3 p4rEBX,p3rEDI

addl $8,%edi ;3 p01rwEDIwF

decl %ecx ;1 p01rwECXwF

jnz L1 ;2 p1rF

Arquitectura de Computadores I. Práctica 3

Page 55: Departamento de Arquitectura y Tecnología de Computadores

60Arquitectura de Computadores I. Práctica 3

Índice

1. Cuestiones generales sobre optimización

2. Optimización de la Captación de Instrucciones

3. Optimización de la Decodificación y el Renombrado de Registros

4. Optimización de la Ejecución

5. Optimización del Acceso a Memoria Principal y Cache

6. Optimización de Saltos

7. Optimización de Bucles

8. Realización de los Ejercicios Prácticos

Page 56: Departamento de Arquitectura y Tecnología de Computadores

61Arquitectura de Computadores I. Práctica 3

Realización de la Práctica

Se utiliza como compilador de C el GCC para DOS/WINDOWS:

www.delorie.com/djgpp

Como es sabido, el proceso de compilación tiene cuatro etapas: preprocesamiento, compilación propiamente dicha, ensamblado, y enlazado (link).

Con las opciones:

-c Se compila o se ensamblan los ficheros fuente utilizados pero no se enlazan. Es decir, se generan los ficheros objeto .o.

-S El proceso de compilación para después de la etapa de compilación propiamente dicha. Por lo tanto, se obtiene el código en

ensamblador .s

Page 57: Departamento de Arquitectura y Tecnología de Computadores

62Arquitectura de Computadores I. Práctica 3

/* Ejemplo de Programa de Prueba */ #include <stdio.h> #include <math.h> #include <time.h> int suma_prod(int a, int b, int n); main() { /* ------------- */ int i,j,a,b,n,c; /* ------------- */ clock_t start,stop; start= clock(); /* ------------------------------------------ */ n=6000;a=1;b=2; for (j=1;j<=10000;j++) { printf("a=%d b=%d n=%d\n",a,b,n); c=suma_prod(a,b,n); printf("resultado= %d\n",c); } /* -------------------------------------------- */ stop = clock(); printf("Tiempo= %f",difftime(stop,start)); return 0; }

Figura A2.1 Código C correspondiente a un programa de base (test_bench.c)

test_bench.c

/* Ejemplo de Funcion */ int suma_prod(int a, int b, int n) { return a*b+n; }

Función a optimizar suma_prod()

Ejemplo de programa de prueba en C

Page 58: Departamento de Arquitectura y Tecnología de Computadores

63

test_bench.c suma_prod.c

Transformaciones Alto Nivel

Transformaciones Ensamblador

gcc –c

test_bench.o

gcc –S

suma_prod.s

gcc –c

suma_prod.o

test_bench

gcc –o test_bench ficheros .o

Esquema de Trabajo en los Ejercicios Prácticos

Arquitectura de Computadores I. Práctica 3

Page 59: Departamento de Arquitectura y Tecnología de Computadores

64Arquitectura de Computadores I. Práctica 3

Opciones de Optimización de GCC (I)

-O1: Con esta opción, el compilador modifica las siguientes alternativas de compilación

-fthread-jumps (comprueba si hay un salto a otra posición donde se encuentra otro salto y si se conoce la condición de salto se redirecciona el primero convenientemente).

-fdefer-pop (evita hacer pop de los argumentos de llamada a una función cuando se retorna de la llamada a la función)

-fdelayed-branch (en máquinas segmentadas con ‘delays slots’, intenta reordenar las instrucciones para eliminar ciclos desperdiciados).

-fomit-frame-pointer (en máquinas que permiten la depuración sin utilizar punteros de pila, indica que no se almacene el puntero de pila en un registro en el caso de funciones que no lo necesiten. Con esto se ahorran las correspondientes instrucciones de almacenamiento y recuperación del puntero).

-O2: Con esta opción se activan todas las alternativas de optimización menos el desenrollado y la opción –finline-functions (integra las funciones sencillas

en donde están sus llamadas).

-O3: Se activan todas las alternativas de optimización.

-Os: Se activan todas las alternativas de –O2 que no suelen incrementar el tamaño del código

Page 60: Departamento de Arquitectura y Tecnología de Computadores

65Arquitectura de Computadores I. Práctica 3

Opciones de Optimización de GCC (II)

Otros ejemplos (además de los indicados anteriormente):

-fforce-mem: fuerza a los operandos en memoria a copiarse en los registros antes de hacer operaciones aritméticas con ellos. Esta alternativa se activa con –O2.

-fforce-addr: fuerza que las direcciones de memoria constantes se copien en registros antes de hacer operaciones aritméticas sobre ellas. Se activa con –O2.

-ffast-math: se permite al gcc que viole algunas reglas y/o especificaciones ANSI o IEEE para conseguir más velocidad. Por ejemplo se asume que los argumentos de la función sqrt son no-negativos y que ningún valor en coma flotante es NaN.

Descripción de las opciones de optimización de gcc (II)

Manual de gcc:

http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc.pdf

Page 61: Departamento de Arquitectura y Tecnología de Computadores

66Arquitectura de Computadores I. Práctica 3

Ejemplo de Códigos en Ensamblador generados con la opción –S (para

suma_prod()) .file "suma_prod.c" gcc2_compiled.: ___gnu_compiled_c: .text .p2align 2 .globl _suma_prod _suma_prod: pushl %ebp movl %esp,%ebp movl 8(%ebp),%edx imull 12(%ebp),%edx addl 16(%ebp),%edx movl %edx,%eax jmp L2 .p2align 4,,7 L2: movl %ebp,%esp popl %ebp ret

.file "suma_prod.c" gcc2_compiled.: ___gnu_compiled_c: .text .p2align 2 .globl _suma_prod _suma_prod: pushl %ebp movl %esp,%ebp movl 8(%ebp),%eax imull 12(%ebp),%eax addl 16(%ebp),%eax movl %ebp,%esp popl %ebp ret

Figura A2.4 Códigos en ensamblador para suma_prod() sin optimizar y con optimización –O1

Page 62: Departamento de Arquitectura y Tecnología de Computadores

67Arquitectura de Computadores I. Práctica 3

Para consultar…………

Repertorio de Instrucciones. Listado de instrucciones y número de microoperaciones. Información precisa sobre la microarquitectura:

http://www.intel.com/design/PentiumIII/manuals/

http://developer.intel.com/design/pentium4/manuals/index.htm

Compilador de C (gcc para DOS/WINDOWS).

http://www.delorie.com/djgpp/

Ensamblador (formato AT&T), directivas, arquitectura, convenciones de llamada:

http://docencia.ac.upc.edu/FIB/PROSO/index_files/Annex-%20Asm.pdf

http://www.delorie.com/djgpp/doc/ug/asm/about-386.html

http://en.wikibooks.org/wiki/Reverse_Engineering/Assemblers#.28x86.29_AT.26T_Syntax_Assemblers

http://www.delorie.com/djgpp/doc/ug/asm/calling.html

Página de Compilación para Pentium

http://www.iti.cs.tu-bs.de/soft/www.goof.com/pcg/