El procesador Pentium es un superescalar, construido alrededor de

Anuncio
ARQUITECTURA DE SISTEMAS PARALELOS 2.
4º INGENIERÍA INFORMÁTICA. PRÁCTICA 4.
PROGRAMACION PARALELA CON openMP.
1.
OBJETIVOS Y PREPARACIÓN.
En la actualidad los multiprocesadores en un solo chip (denominados habitualmente procesadores de múltiple
núcleo) están extendiéndose de manera muy notable. Si queremos aprovechar las posibilidades de estos procesadores
es necesario particionar nuestro código en diversos hilos de forma que puedan ejecutarse simultáneamente tanto hilos
como procesadores tengamos. Con el fin de facilitar este proceso se han introducido herramientas que permiten
realizar esta partición de manera sencilla sin tenerse que preocupar de crear y destruir los hilos o de gestionar las
comunicaciones entre ellos de forma explicita. Dentro de estas herramientas una de las más populares es el entorno
openMP que hoy en día está soportado en la mayoría de multiprocesadores con memoria compartida. En la familia
80x86 openMP está soportado los compiladores de C++ de Microsoft e Intel (Windows y Linux) y otros.
La referencia de openMP podemos encontrarla en http://www.openmp.org/drupal/node/view/8 y un buen
tutorial en español en http://www.arcos.inf.uc3m.es/~ii_ac2/04-05/OpenMP_curso.pdf
OpenMP utiliza para generar el código paralelo directivas y funciones insertadas en el código C++ (o
Fortran). En C la directivas usan el formato:
#pragma omp directiva [comando[(parámetros)], [comando[(parámetros)]…
Tanto las directivas como las funciones no son tenidas en cuenta si no habilitamos la compatibilidad openMP
en el compilador. En Visual Studio la opción para habilitar está compatibilidad está, dentro de las propiedades del
proyecto en: Propiedades de Configuración -> C/C++ -> Lenguaje -> Compatibilidad con openMP.
La práctica estudiará la optimización del código SAXPY ya empleado en la práctica 3 en un multiprocesador.
Para completarlo introduciremos un nuevo ejemplo con dependencias a través del bucle: el producto escalar.
Para realizar estos ejemplos sólo necesitamos las directivas de openMP referentes a la optimización de bucles
for . Estas son (ver tutorial):
• #pragma omp parallel
• #pragma omp for
• Cláusula combinada que une las dos anteriores: #pragma omp parallel for
Dentro de esta directiva se puede especificar (ver tutorial)
• Variables locales a cada thread (private()) y compartidas entre todos (shared()). Por defecto la
variable índice del bucle es privada y las demás son compartidas.
• Políticas de partición de carga (static o dynamic)
• Posibilidad de operadores de reducción.
2.
REALIZACIÓN DE LA PRÁCTICA.
2.1. PRUEBA 1: Comparación Inicial.
1. Obtener las características del procesador empleado usando cpuz 1.40 (descargarlo de
http://www.cpuid.com/ )
2. Abrir el entorno Visual Studio .NET y crear un proyecto de consola WIN32 vacío. Añadir un archivo de
utilidad TXT y nombrarlo con extensión “.c”. Copiar el código fuente que se proporciona en el enunciado de esta
práctica. Poner el compilador en modo release y no habilitar openMP. Apuntar los tiempos en la tabla de resultados.
2.2. PRUEBA 2: OpenMP básico.
1. Añadir las directivas para paralelizar los bucles del SAXPY y del producto escalar. Probar con un número
de threads entre 1 y el número de núcleos (o hyperthreads) del procesador. Anotar resultados.
2.3. PRUEBA 3: Políticas de secuenciamiento.
1. Probar políticas de secuenciamiento estáticas y dinámicas con al menos 2 tamaños de partición de datos
(chunk). Anotar resultados.
Interpretación de Resultados:
Realizar una interpretación cualitativa de los resultados obtenidos en las diversas pruebas en función de las
características del procesador y:
• Habilitación o no de openMP.
• Número de Threads.
• Tamaño de los vectores.
• Políticas de secuenciamiento y partición.
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
Explicar las diferencias entre los tiempos del SAXPY y los del producto escalar.
ARQUITECTURA DE SISTEMAS PARALELOS II. PRACTICA 4
ANÁLISIS DE INSTRUCCIONES DEL NÚCLEO MULTIMEDIA.
NUM GRUPO:
ALUMNOS:
Características del Ordenador.
Procesador (Modelo
/Frecuencia/Núcleos/HT)
Cache(s) de nivel 1º.
Cache de Nivel 2º
Memoria Principal
Chipset
TAM
Tabla de resultados SAXPY básico:
omp 1 thread
Sin omp
tiempo
aceleración
L1/2 elem
1.0
L1 elem
1.0
2*L1 elem
1.0
L2/4 elem
1.0
L2/2 elem
1.0
L2 elem
1.0
2*L2 elem
1.0
TAM
Tabla de resultados PE básico:
Sin omp
tiempo
aceleración
L1/2 elem
1.0
L1 elem
1.0
2*L1 elem
1.0
L2/4 elem
1.0
L2/2 elem
1.0
L2 elem
1.0
2*L2 elem
1.0
tiempo
Omp__ threads
aceleración
omp 1 thread
tiempo
tiempo
aceleración
Omp__ threads
aceleración
tiempo
aceleración
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
Tabla de resultados SAXPY (políticas secuenciamiento. Indicar Política y tamaño partición):
TAM
tiempo
aceleración
tiempo
aceleración
tiempo
aceleración
tiempo
aceleración
tiempo
aceleración
L1/2 elem
L1 elem
2*L1 elem
L2/4 elem
L2/2 elem
L2 elem
2*L2 elem
Tabla de resultados PE (políticas secuenciamiento. Indicar Política y tamaño partición):
TAM
tiempo
aceleración
tiempo
aceleración
tiempo
aceleración
L1/2 elem
L1 elem
2*L1 elem
L2/4 elem
L2/2 elem
L2 elem
2*L2 elem
Justificación de resultados:
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
ESQUELETO DEL CÓDIGO DE PRUEBA
#include <stdio.h>
#include <math.h>
#include <omp.h>
#define
#define
#define
#define
#define
MHZ 3E3
VSIZE (512)
REPETICIONES (1024)
N 256
TAM VSIZE*N
__declspec(align(16)) float x[TAM], y[TAM], z[TAM], m[4] = {1.0, 2.0, 3.0, 4.0};
// alinea en frontera de 16 bytes los vectores.
float comp[TAM]; //vector para comprobar resultados
float a = (float) 3.14159;
float b = 0.5,pe;
int ciclos[2];
void codigo_c (int tam);
float codigo_cpe (int tam);
//inicialización con instr. SSE: x[] = {1, 2, 3, 4, 1, 2, 3, 4,.. }, y[i] = sqrt(x[i])
void inic (int tam);
int main(void) {
int i;
//omp_set_num_threads(2); //Fijar numero de Threads
for (i=1; i<=N; i*=2) {
printf("\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
printf("\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
printf("\n\nCodigo C para %d elementos\n", i*VSIZE);
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
codigo_c (i*VSIZE); //codigo C en otra funcion para que no influyan las
optimizaciones del compilador
//--------------------------------------------------------------------------printf( " -> Elem primero y ultimo: %.5e , %.5e \n", y[0], y[i*VSIZE-1]);
//--------------------------------------------------------------------------pe=codigo_cpe (i*VSIZE);
//--------------------------------------------------------------------------printf( " -> Producto Escalar %.5e \n", pe);
//---------------------------------------------------------------------------
printf("\n\n");
}
return 0;
}
//***********************************************************************
//
FUNCIONES
//***********************************************************************
//codigo C en
void codigo_c
int i,
int min =
otra funcion para que no influyan las optimizaciones del compilador
(int tam) {
j;
0x7fffffff;
inic (tam);
for (j=0; j<REPETICIONES; j++) {
__asm {
lea esi, ciclos
mfence
rdtsc
mov [esi], eax
mov [esi+4], edx
//Codigo C (del SAXPY O SIMILAR)
//#pragma omp
----- AÑADIR DIRECTIVA
for (i=0; i<tam; i++)
y[i] = a*x[i] + y[i];
// Fin codigos de prueba
__asm {
mfence
rdtsc
lea esi, ciclos
sub eax, [esi]
sbb edx, [esi+4]
mov [esi], eax
mov [esi+4], edx
mfence
}
if (min > ciclos[0])
min = ciclos[0];
}
printf( "
--> %u ciclos => %.3lf useg
\n", min, min / MHZ);
//Guardo resultados correctos en vector comp[]
for(i=0;i<tam;i++)
comp[i] = y[i];
}
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
float codigo_cpe (int tam) {
int i, j;
int min = 0x7fffffff;
float res;
inic (tam);
for (j=0; j<REPETICIONES; j++) {
__asm {
lea esi, ciclos
mfence
rdtsc
mov [esi], eax
mov [esi+4], edx
}
// Codigos de prueba
//Codigo C (Producto escalar)
res=0;
//#pragma omp --- Añadir directiva
for (i=0; i<tam; i++)
res+=x[i] * y[i];
// Fin codigos de prueba
__asm {
mfence
rdtsc
lea esi, ciclos
sub eax, [esi]
sbb edx, [esi+4]
mov [esi], eax
mov [esi+4], edx
mfence
}
if (min > ciclos[0])
min = ciclos[0];
}
printf( "
--> %u ciclos => %.3lf useg
\n", min, min / MHZ);
//Guardo resultados correctos en vector comp[]
return res;
}
void inic (int tam) {
__asm
{
lea eax, m
lea ebx, x
lea edx, y
mov edi, tam
shl edi, 2
mov ecx, ebx
add ecx, edi
movaps xmm0, [eax]
sqrtps xmm1, xmm0
lp:
movaps [ebx], xmm0
movaps [edx], xmm1
add
ebx, 16
add edx, 16
cmp ebx, ecx
jne lp
}
Arquitectura de Sistemas Paralelos 2. Dpto. ATC. Práctica 4.
Descargar