09.05.2013 Views

Tema 5: El Pentium a Fondo - DAC

Tema 5: El Pentium a Fondo - DAC

Tema 5: El Pentium a Fondo - DAC

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

MICROPROCESADORES<br />

<strong>Tema</strong> 5: <strong>El</strong> <strong>Pentium</strong> a <strong>Fondo</strong><br />

Carlos Garre y David Miraut<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

1


Contenidos<br />

Programación Eficiente<br />

MICROPROCESADORES<br />

• Programación eficiente en arquitecturas <strong>Pentium</strong>:<br />

– Análisis de rendimiento.<br />

– Programación con extensiones SIMD.<br />

– Introducción a programación concurrente.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

2


Contenidos<br />

Análisis de Rendimiento<br />

MICROPROCESADORES<br />

• CPU Profiler: Herramienta que permite analizar el tiempo que<br />

consume una tarea, y qué funciones o threads de esa tarea son los<br />

que están consumiendo más tiempo de CPU.<br />

• Uso de un profiler:<br />

1. Ejecutar la aplicación que queremos analizar, lanzándola desde el<br />

profiler.<br />

2. Mientras la aplicación se ejecuta, el profiler toma muestras de tiempos y<br />

de llamadas entre los componentes del programa.<br />

3. Cuando termina la ejecución, el profiler muestra resultados estadísticos,<br />

informando de qué funciones son las que más recursos consumen.<br />

4. Teniendo localizados los cuellos de botella de nuestro código,<br />

optimizamos esas líneas de código concretas.<br />

5. Algunos profilers (Intel VTune) pueden incluso decirnos exactamente<br />

cómo podemos optimizar ese código.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

3


Contenidos<br />

Intel VTune<br />

MICROPROCESADORES<br />

• Intel VTune: Es un profiler muy avanzado de Intel que permite<br />

aprovechar al máximo todas las características de las arquitecturas de<br />

Intel (IA-32, IA-32/64 bits, IA-64…).<br />

• Trabaja junto con el Intel Compiler, y se puede integrar totalmente con<br />

Visual Studio.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

4


Contenidos<br />

Sampling Wizard<br />

MICROPROCESADORES<br />

• Sampling Configuration Wizard: Herramienta que nos permite rápidamente<br />

analizar el consumo de todas las tareas que se están ejecutando en el<br />

ordenador en ese momento.<br />

• Si hacemos doble-click en la tarea que corresponde a nuestro programa,<br />

podemos ver el código fuente (si fue compilado con información de<br />

depuración).<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

5


• Si utilizamos el botón “Get<br />

Advice Using Intel VTune<br />

Assistant”, VTune nos dará<br />

consejos sobre cómo optimizar<br />

el código seleccionado.<br />

Contenidos<br />

Assistant Advice<br />

MICROPROCESADORES<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

6


Call Graph<br />

MICROPROCESADORES<br />

• VTune nos puede mostrar gráficamente qué funciones de nuestro código están<br />

consumiendo más recursos. Para ello utilizamos la herramienta Call Graph.<br />

• Nos muestra un árbol jerárquico, en el que se desdoblan las ramas que<br />

corresponden a las funciones más pesadas. <strong>El</strong> color de las hojas del árbol indica el<br />

consumo (los colores más cercanos a rojo indican un mayor consumo).<br />

Contenidos<br />

• Desde el árbol también podemos abrir el código fuente de la función que<br />

queramos.<br />

• Vamos a ver a continuación algunos de los consejos típicos que nos puede dar<br />

VTune, y cómo seguirlos.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

7


Conversiones de Tipos<br />

MICROPROCESADORES<br />

• Utilizar los tipos adecuados para cada variable. No basta sólo con distinguir entre<br />

enteros y reales, sino también entre los tipos de enteros: short, int, long.<br />

• Si se utilizan librerías externas (math.h), comprobar los tipos que piden las<br />

funciones que estemos utilizando.<br />

• Vigilar los tipos en las variables que iteran los bucles.<br />

• A veces los warning del compilador también nos pueden avisar de posibles<br />

problemas con los tipos. En general, deberíamos dejar el compilador sin ningún<br />

warning, ya que no sólo informan de errores potenciales sino también de posibles<br />

problemas de rendimiento.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

8


Desenrollado de bucles<br />

MICROPROCESADORES<br />

• Los bucles con pocas instrucciones por iteración (pero muchas iteraciones), no<br />

aprovechan las posibilidades de paralelismo del procesador.<br />

Contenidos<br />

• Para aumentar el paralelismo, y evitar la sobrecarga de los continuos saltos, se<br />

pueden desenrollar los bucles para realizar más operaciones por cada iteración:<br />

for (i = 1; i > 1;<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

for (i = 1; i > 1;<br />

data [i+1] = data [i+1] >> 1;<br />

data [i+2] = data [i+2] >> 1;<br />

data [i+3] = data [i+3] >> 1;<br />

}<br />

9


Desenrollado de bucles (II)<br />

MICROPROCESADORES<br />

• Si el número de iteraciones no cuadra con el factor de desenrollamiento, se<br />

pueden hacer dos bucles consecutivos.<br />

• No se deben desenrollar bucles con pocas iteraciones o con muchas<br />

instrucciones.<br />

Contenidos<br />

• Otras consideraciones para optimizar bucles:<br />

Introducir sólo instrucciones que dependan de las iteraciones del bucle. <strong>El</strong> código<br />

invariante debe salir fuera del bucle.<br />

Si dentro de un bucle hay una sentencia condicional (if-then-else) que no depende del<br />

bucle, se saca fuera:<br />

For (i…)<br />

If (a == b)<br />

<strong>El</strong>se<br />

End For;<br />

Sentencias1;<br />

Sentencias2;<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

If (a == b)<br />

For (i…)<br />

Sentencias1;<br />

End For;<br />

<strong>El</strong>se<br />

For (i…)<br />

Sentencias2;<br />

End For;<br />

10


• Bucle sin desenrollar:<br />

Contenidos<br />

for( i = 0; i < n; i++ )<br />

a[ i ] = b * c[ i ];<br />

Desenrollado de bucles (III)<br />

• Bucle desenrollado con factor 4:<br />

ii = n % 4;<br />

for( i = 0; i < ii; i++ )<br />

a[ i ] = b * c[ i ];<br />

for( i = ii; i < n; i += 4 )<br />

{<br />

}<br />

a[ i ] = b * c[ i ];<br />

a[ i+1 ] = b * c[ i+1 ];<br />

a[ i+2 ] = b * c[ i+2 ];<br />

a[ i+3 ] = b * c[ i+3 ];<br />

MICROPROCESADORES<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

11


Manejo de Arrays<br />

MICROPROCESADORES<br />

• Cuando queremos inicializar un array con un valor fijo, es mucho más eficiente<br />

hacerlo con memset que con un bucle:<br />

for (i = 0; i < n; i++)<br />

salida [i] = 0;<br />

memset (salida, 0, n*sizeof(short));<br />

• Para copiar un array sobre otro, es mucho más eficiente hacerlo con memcpy que<br />

con un bucle:<br />

for (i = 0; i


Otros consejos de Optimización<br />

MICROPROCESADORES<br />

• Precalcular todo lo que pueda ser precalculado.<br />

• En sentencias condicionales anidadas, poned siempre primero las que sean<br />

menos costosas y más probables. Las sentencias switch-case suelen ser más<br />

eficientes que las if-then-else anidadas.<br />

• Evitar la aritmética costosa siempre que sea posible:<br />

2*a = a + a.<br />

Log(a) + Log(b) = Log(a*b).<br />

Pot(a,2) = a*a;<br />

a/2 = a >> 1;<br />

• Utilizar estructuras de datos eficientes. Librería STL de C++:<br />

¿ vector a ó int a[] ?.<br />

• Compilar en modo debug para buscar warnings y eliminarlos.<br />

• Vectorización mediante extensiones SIMD (el próximo apartado):<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

13


Contenidos<br />

Programación Eficiente<br />

MICROPROCESADORES<br />

• Programación eficiente en arquitecturas <strong>Pentium</strong>:<br />

– Análisis de rendimiento.<br />

– Programación con extensiones SIMD.<br />

– Introducción a programación concurrente.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

14


Intrinsics<br />

MICROPROCESADORES<br />

• Funciones incluidas directamente en el compilador, que te permiten acceder a<br />

funciones de bajo nivel sin necesidad de programar directamente en<br />

ensamblador.<br />

• Son más eficientes que una librería de funciones, ya que te ahorras realizar<br />

llamadas a subrutinas se genera directamente código ensamblador en el punto<br />

donde se encuentra la Intrinsic.<br />

• Son muy dependientes del compilador problema de portabilidad.<br />

• #include “mmintrin.h” Nos facilita Intrinsics para aprovechar la extensión<br />

MMX:<br />

– Nuevo tipo de datos para utilizar los registros MMX:<br />

• __m64: entero de 64 bits. __m64* p_mmx<br />

– Juego de instrucciones SIMD de la extensión MMX.:<br />

• PADDB: __m64 _mm_add_pi8 (__m64 m1 , __m64 m2);<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

15


MMX: Funciones Aritméticas<br />

MICROPROCESADORES<br />

• __m64 _mm_adds_pi8 (__m64 m1, __m64 m2): Suma por separado los 8 bytes<br />

de un registro MMX con los 8 bytes de otro registro MMX.<br />

• __m64 _mm_sub_pi32 (__m64 m1, __m64 m2): Resta la parte alta del registro<br />

m1 de la parte alta del registro m2, y la parte baja del registro m1 de la parte baja<br />

del registro m2.<br />

• __m64 _mm_madd_pi16 (__m64 m1, __m64 m2): Multiplica las 4 palabras de 16<br />

bits del registro m1 por las 4 palabras del registro m2. Esto produce 4 valores<br />

intermedios de 32 bits. Estos 4 valores se suman dos a dos, para producir 2<br />

valores de 32 bits.<br />

• __m64 _mm_mulhi_pi16 (__m64 m1, __m64 m2): Multiplica las 4 palabras de<br />

m1 por las de m2, y devuelve la parte alta de cada resultado. Para la parte baja<br />

está la función _mm_mullo_pi16.<br />

• __m64 _mm_add_pu8 (__m64 m1, __m64 p2): Considera que todos los valores<br />

son enteros sin signo (unsigned). Resultado no saturado.<br />

*p_mmx = _mm_adds_pi8 (*m1, *m2);<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

16


MMX: Funciones Lógicas y de Bit<br />

MICROPROCESADORES<br />

• __m64 _mm_sll_pi16 (__m64 m, __m64 count): Hace un desplazamiento lógico<br />

de bits a la izquierda por separado a las 4 palabras de 16 bits del registro m. <strong>El</strong><br />

número de bits que se desplazan se especifica en count.<br />

• __m64 _mm_sra_pi16 (__m64 m, __m64 count): Hace un desplazamiento<br />

aritmético a la derecha. <strong>El</strong> desplazamiento aritmético respeta el bit de signo <br />

introduce bit de signo por la izquierda, en lugar de ceros.<br />

• __m64 _mm_and_si64 (__m64 m1, __m64 m2): Hace un AND entre todos los bits<br />

de los registros m1 y m2. También está el equivalente con OR y XOR. No existe<br />

versión con menos bits no es realmente una instrucción SIMD.<br />

• __m64 _mm_andnot_si64 (__m64 m1, __m64 m2): Realiza la operación NOT(m1)<br />

AND m2.<br />

• __m64 _mm_cmpeq_pi8 (__m64 m1, __m64 p2): Compara cada byte de los 2<br />

registros. Pone 11111111 en cada par de bytes que sean iguales, y 00000000 en<br />

los que sean diferentes. También existe cmpgt, y ambas con 8, 16 y 32 bits.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

17


MMX: Funciones de Inicialización<br />

MICROPROCESADORES<br />

• __m64 _mm_setzero_si64 (): Carga 0 en los 64 bits de un registro MMX.<br />

*p_mmx = _mm_setzero_si64();<br />

• __m64 _mm_set_pi32 (int i1, int i2): Carga el valor i1 sobre la parte alta del<br />

registro MMX, e i2 sobre la parte baja.<br />

• __m64 _mm_set1_pi8 (byte b): Carga el mismo valor sobre los 8 bytes que<br />

componen el registro MMX.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

18


MMX: Otras Funciones<br />

MICROPROCESADORES<br />

• void __mm_empty (void): Instrucción EMMS.<br />

Vacía el contenido de los 8 registros compartidos entre la FPU y la extensión MMX.<br />

Sirve para realizar un cambio de contexto entre ambos.<br />

Es muy costosa minimizar su uso agrupando convenientemente el código.<br />

Si estamos utilizando MMX, se debe usar esta antes de utilizar cualquier instrucción de<br />

la FPU.<br />

• __m64 _mm_cvtsi32_si64_si64 (int i): Convierte un valor entero de 32 bits en un<br />

valor de 64 bits para un registro MMX.<br />

• int _mm_cvtsi64_si32 (__m64 m): Convierte el valor de 64 bits de un registro<br />

MMX a un entero de 32 bits.<br />

• __m64 _mm_packs_pi32 (__m64 m1, __m64 m2): Empaqueta los dos valores de<br />

de 32 bits de m1 en los 2x16 bits menos significativos del resultado, y los dos<br />

valores de 32 bits de m2 en los 2x16 bits más significativos del resultado.<br />

• __m64 _mm_unpackhi_pi32 (__m64 m1, __m64 m2): La parte alta del resultado<br />

es la parte alta de m2, y la parte baja es la parte alta de m1.<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

19


MMX: Ejemplos<br />

MICROPROCESADORES<br />

• Desplazamiento aritmético a la derecha (división por 2) de 4 enteros, partiendo de<br />

un bucle desenrollado:<br />

for (i = 1; i > 1;<br />

data [i+1] = data [i+1] >> 1;<br />

data [i+2] = data [i+2] >> 1;<br />

data [i+3] = data [i+3] >> 1;<br />

}<br />

• Operación MAC:<br />

r1 = wr*data[j+1] – wi*data[j];<br />

r2 = wr*data[j+1] + wi*data[j];<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

for (i = 1; i


• SSE:<br />

#include “xmmintrin.h”<br />

Intrinsics para SSE y SSE2<br />

Nuevos tipos de datos: __mm128.<br />

MICROPROCESADORES<br />

__m128 __mm_add_ps (__m128 a, __mm128 b): Suma por separado los 4 valores en<br />

IEEE-754 de precisión simple (32 bits) de los registros XMM a y b.<br />

__mm_add_pd: doble precisión.<br />

• SSE 2:<br />

#include “emmintrin.h”<br />

Instrucciones para carga y almacenamiento de valores en coma flotante.<br />

Instrucciones con enteros de 128 bits.<br />

Instrucciones para manejo de la caché.<br />

Etc…<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

21


float* Vector1, Vector2, Resultado;<br />

int dimension = 100;<br />

for (int i = 0; i < dimension; i++ )<br />

{<br />

}<br />

*Resultado = (float)sqrt((*Vector1) *<br />

(*Vector1) + (*Vector2) * (*Vector2)) + 0.5f;<br />

Vector1++;<br />

Vector2++;<br />

Resultado++;<br />

Código adaptado de:<br />

http://www.codeproject.com/KB/recipes/sseint<br />

ro.aspx<br />

SSE: Ejemplo<br />

MICROPROCESADORES<br />

float* Vector1, Vector2, Resultado;<br />

int dimension = 100;<br />

int n_iter = dimension/ 4;<br />

__m128 m1, m2, m3, m4;<br />

__m128* pSrc1 = (__m128*) Vector1;<br />

__m128* pSrc2 = (__m128*) Vector2;<br />

__m128* pDest = (__m128*) Resultado;<br />

__m128 m0_5 = _mm_set_ps1(0.5f); // m0_5[0, 1, 2, 3] = 0.5<br />

for ( int i = 0; i < n_iter; i++ )<br />

{<br />

m1 = _mm_mul_ps(*pSrc1, *pSrc1); // m1 = *pSrc1 * *pSrc1<br />

m2 = _mm_mul_ps(*pSrc2, *pSrc2); // m2 = *pSrc2 * *pSrc2<br />

m3 = _mm_add_ps(m1, m2); // m3 = m1 + m2<br />

m4 = _mm_sqrt_ps(m3); // m4 = sqrt(m3)<br />

*pDest = _mm_add_ps(m4, m0_5); // *pDest = m4 + 0.5<br />

}<br />

pSrc1++;<br />

pSrc2++;<br />

pDest++;<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

22


Práctica 2<br />

MICROPROCESADORES<br />

• Optimización mediante extensiones MMX<br />

• Segunda práctica obligatoria.<br />

• Práctica por parejas.<br />

• Campus Virtual:<br />

Enunciado.<br />

Material de apoyo.<br />

Ejemplos de código.<br />

Fechas de entrega.<br />

• Se os dará un código ya hecho, que debéis optimizar<br />

utilizando extensiones SIMD y cualquier otro tipo de<br />

optimización bien documentada.<br />

• Si se detecta copia ¡Suspensa toda la convocatoria!<br />

ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA<br />

INFORMÁTICA<br />

23

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!