departamento de arquitectura y tecnología de computadores
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 PresentationTRANSCRIPT
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
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.
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
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
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
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í).
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/
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
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
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
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.
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
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
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
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
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
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
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)
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.)
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)
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).
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)
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
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
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
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!!)
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
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]
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.
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
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
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.
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.
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)
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)
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
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
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
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
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
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
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
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:
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;}
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
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
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
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
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
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
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.
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
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
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
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
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
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
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
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
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
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
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/