Tema 5: El Pentium a Fondo - DAC
Tema 5: El Pentium a Fondo - DAC
Tema 5: El Pentium a Fondo - DAC
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
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