Openmp

  • Uploaded by: Fernando Félix-Redondo
  • 0
  • 0
  • April 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Openmp as PDF for free.

More details

  • Words: 2,443
  • Pages: 39
OpenMP: API para escribir aplicaciones SMP portátiles Fernando Félix-Redondo Universidad Carlos III Departamento de Informática

Índice  Introducción: OpenMP a vista de pájaro  Sintaxis básica: Empezando con OpenMP  “Constructores” de OpenMP:  Regiones paralelas  Trabajo compartido (Worksharing)  Entorno de datos  Sincronización  Funciones en tiempo de ejecución y variables de entorno

 Evitando errores de programación SMP  Compiladores OpenMP

OpenMP a vista de pájaro 

Para programar sistemas con memoria distribuida: 



Hay que usar “bibliotecas de funciones” para paso de mensajes como MPI o PVM

Para los sistemas de memoria compartida: 

Es posible también usar paso de mensajes pero …



Existen soluciones basadas directamente en el paradigma de memoria compartida. Ejemplo: OpenMP

Shared Memory

OpenMP a vista de pájaro 

OpenMP es un API para escribir aplicaciones multihilos (multithreaded)   



Conjunto de directivas de compilación, algunas funciones de biblioteca y unas pocas variables de entorno

OpenMP es básicamente lo mismo en C/C++ y en Fortran

OpenMP

Directivas de compilación

Funciones de Librería en tiempo de ejecución

Variables de Entorno

#pragma omp parallel for omp_get_thread_num() OMP_NUM_THREADS

OpenMP a vista de pájaro 



Modelo de programación denominado “Paralelismo Fork-Join” 

El hilo maestro arranca un conjunto de hilos cuando se necesita paralelizar una determinada tarea



Al terminar su labor, los hilos creados terminan y continua la ejecución secuencial

Generalmente, el paralelismo se añade de forma incremental a un programa secuencial pre-existente

OpenMP a vista de pájaro 



OpenMP se usa habitualmente para paralelizar bucles 

Se localizan los bucles que consumen más tiempo



Se reparten entre los distintos hilos o threads

double V[10000]; for (int i=0; i< 10000; i++) { calculo_brutal( V[i] ); }

Hay que evitar las condiciones de carrera entre hilos 

La compartición de datos de forma no intencionada puede dar problemas



Se puede usar sincronización para evitar conflictos de datos



La sincronización es “cara” por lo que…



…hay que distribuir los datos entre los hilos con inteligencia

double V[10000]; #pragma omp parallel for for (int i=0; i< 10000; i++) { calculo_brutal( V[i] ); }

Índice  Introducción: OpenMP a vista de pájaro  Sintaxis básica: Empezando con OpenMP  “Constructores” de OpenMP:  Regiones paralelas  Trabajo compartido (Worksharing)  Entorno de datos  Sincronización  Funciones en tiempo de ejecución y variables de entorno

 Evitando errores de programación SMP  Compiladores OpenMP

Sintaxis básica: Empezando con OpenMP 

Como primer paso hay que incluir el fichero de cabeceras al principio del programa en C/C++: #include

#include using namespace std; int main(int argc, char* argv[]) { omp_set_num_threads(10); // Número de Hilos #pragma omp parallel // Ejecuta en paralelo a partir de aquí cout << "Hello World!!" << endl; // Aquí de nuevo es secuencial la ejecución return 0; }

Sintaxis básica: Constructores 

Los “constructores” de OpenMP son básicamente directivas de compilación (pragmas) #pragma omp construct [clause [clause]…]

 

Un compilador NO-OpenMP simplemente ignorará las “pragmas” Con OpenMP, es posible que el mismo código fuente sirva para la versión secuencial y paralela de un programa

Sintaxis básica: Compilación condicional #include #include using namespace std; int main(int argc, char* argv[]) { int nthreads=1; #pragma omp parallel // Ejecuta en paralelo a partir de aquí { cout << "Hello World!!" << endl;

Si se usa OpenMP, estará definida

#ifdef _OPENMP nthreads=omp_get_num_threads(); // Averiguar el número de Hilos

#endif cout << "Somos " << nthreads << " threads" << endl; } // Aquí de nuevo es secuencial la ejecución return 0; }

Sintaxis básica: Bloques estructurados 



La mayoría de los constructores OpenMP se aplican sobre bloques estructurados Bloque estructurado: Bloque de código con un único punto de entrada al principio y un único punto de salida al final; la única excepción es exit()

double V[10000], Z[10000];

#pragma omp parallel for for (int i=0; i< 10000; i++) { calculo_brutal( V[i] ); Z[i]=func(i)*sqrt(V[i] }

double V[10000], Z[10000];

#pragma omp parallel for for (int i=0; i< 10000; i++) { calculo_brutal( V[i] ); if (cond(V[i])) goto salida; Z[i]=func(i)*sqrt(V[i] }

Índice  Introducción: OpenMP a vista de pájaro  Sintaxis básica: Empezando con OpenMP  “Constructores” de OpenMP:  Regiones paralelas  Trabajo compartido (Worksharing)  Entorno de datos  Sincronización  Funciones en tiempo de ejecución y variables de entorno

 Evitando errores de programación SMP  Compiladores OpenMP

Constructores OpenMP 

Tenemos 5 categorías de constructores OpenMP: 

Regiones paralelas



Trabajo compartido (worksharing)



Entorno de datos



Sincronización



Funciones “runtime” y variables de entorno

Regiones paralelas 



La expresión “regiones paralelas” se podría traducir, simplemente, como “hilos” (threads)

Este constructor permite crear un conjunto de hilos (threads) en OpenMP double V[10000]; omp_set_num_threads(4);

Cada thread ejecutará el código perteneciente al bloque y llama a “hazalgo” con un ID diferente

#pragma omp parallel { int ID =omp_thread_num(); hazalgo(ID,V);

} printf(“done”);

Regiones paralelas

double V[10000]; omp_set_num_threads(4);

#pragma omp parallel {

double V[10000]

int ID =omp_thread_num(); boo(ID,V);

omp_set_num_threads(4)

hazalgo(0,V)

hazalgo(1,V)

} printf(“done”);

hazalgo(2,V)

hazalgo(3,V)

printf(“done”)

Los threads esperan aquí a que todos hayan terminado (sinc. barrera)

Una sola copia de V es compartida por todos los hilos

Regiones paralelas: Algunos detalles 

Modo dinámico (por defecto):  El

número de hilos puede cambiar de una región paralela a otra

 Fijar

el número de hilos, solo establece el número máximo de hilos posible – podrían obtenerse menos



Modo estático:  El



número de hilos es fijo y controlado por el programador

OpenMP permite anidar regiones paralelas, pero…  El

compilador puede decidir serializar la región anidada

Trabajo compartido (Worksharing) 



El constructor “for” permite dividir las iteraciones de un bucle entre diferentes hilos

Por defecto hay una barrera al final del “omp for” Se puede usar la cláusula “nowait” para desactivarla double V[10000];

#pragma omp parallel #pragma omp for for (int i=0; i< 10000; i++) { calculo( V[i] ); }

Trabajo compartido: Ejemplo ilustrativo for (i=0;i
Trabajo compartido: Cláusula schedule 

Esta cláusula permite controlar como repartimos las iteraciones del bucle entre los threads 

schedule(static [,#n]) 



schedule(dynamic [,#n]) 



Cada hilo toma “#n” iteraciones de una cola hasta que todas se han acabado.

schedule(guided[,#n]) 



Reparte bloques de iteraciones de tamaño “#n” a cada hilo.

Los hilos dinámicamente toman bloques de iteraciones. El tamaño del bloque al principio es grande y disminuye hasta “#n” a medida que progresa la ejecución.

schedule(runtime) 

La política schedule y el tamaño de bloque se toma de la variable de entorno OMP_SCHEDULE. 

(Ejemplo: export OMP_SCHEDULE=“dynamic,10”)

Trabajo compartido: cláusula if









Si las iteraciones no suponen “suficiente trabajo” no tiene sentido ejecutarlas en paralelo Interesa que la decisión sobre si se paraleliza o no se tome en tiempo de ejecución Con “if” el bucle se ejecutará en paralelo si la condición “cond” se evalúa como VERDAD Si no, el bucle se ejecuta secuencialmente, sin incurrir en ninguna penalización por overhead.

double V[10000]; #pragma omp parallel for \ if (N> 1000) for (int i=0; i< N; i++) { calculo(V,i); }

El compilador incluye por sí mismo el código necesario para implementar la sentencia condicional

Trabajo compartido: Anidamiento de bucles 

Cuando uno de los bucles del anidamiento tiene una cláusula parallel for, ésta solo aplica al que le sigue inmediatamente int N=1000; int M=1000;

int N=1000; int M=1000;

#pragma omp parallel for for (int i=0; i< N; i++) {

for (int i=0; i< N; i++) { // Some operations here

// Some operations here for (int j=0;j<M;j++) { // Additional stuff } }

#pragma omp parallel for for (int j=0;j<M;j++) { // Additional stuff } }

Restricciones a los bucles paralelos 

OpenMP establece algunas restricciones sobre los bucles que pueden ser paralelizados 

Debe ser posible conocer el número de iteraciones en tiempo de ejecución antes de comenzarlo



El índice debe ser un integer



El incremento debe ser siempre igual



No se puede usar break o goto para salir del bucle; sí es posible usar goto dentro del propio bucle



Es posible usar continue



Si se generan excepciones en C++, éstas deben ser tratadas dentro del bloque del bucle

Trabajo compartido: constructor sections 





Permite paralelizar una secuencia de tareas sin dependencias de datos entre ellas Asignamos cada tarea a un hilo diferente Es útil cuando resulta difícil paralelizar cada una de las tareas individualmente

#pragma omp parallel sections { X_calculo(); #pragma omp section Y_calculo(); #pragma omp section Z_calculo(); } Barrera implícita

{

X_calculo

Y_calculo

Z_calculo

} Barrera impícita

Trabajo compartido: constructor single 





En ocasiones las regiones paralelas contienen tareas que deberían ser ejecutadas por un único hilo Un ejemplo típico lo constituye la entrada/salida dentro de una región paralela que debe ejecutarse secuencialmente El constructor single también incluye una barrera al final del bloque (a no ser que se use nowait)

#pragma omp parallel { #pragma omp single { leer_datos(X); }

Barrera implícita

calcular(X); calcular_mas() #pragma omp single nowait { escribir_resultado(X); } }

Aquí no hay barrera

Entorno de datos 

Modelo de programación de Memoria Compartida 



Las variables globales son compartidas entre los threads 



La mayoría de las variables están compartidas por defecto

En C: Variables estáticas (static) o globales cuyo ámbito es el fichero

Pero no todo está compartido… 



Las variables de pila en subprogramas llamados desde las regiones paralelas son PRIVADAS Las variables automáticas dentro de un bloque son PRIVADAS

Controlar la compartición de datos  

Es fundamental que los hilos puedan también usar variables privadas. OpenMP tiene cláusulas para controlar el ámbito de los datos dentro de los constructores paralelos int N=1000;  shared

int M=1000;

 private

#pragma omp parallel for for (int i=0; i< N; i++) {

 firstprivate

y lastprivate

a=a+10;

 default

for (int j=0;j<M;j++) { // Additional stuff }

 reduction }

Entorno de datos: cláusula shared 

Establece que las variables especificadas serán compartidas por todos los hilos. 



Sólo existe una instancia de cada una de esas variables

Cualquier modificación por parte de uno de los hilos, actualizará la instancia compartida. 

Cualquier cambio sobre la variable de uno de los hilos es visto por los demás

Entorno de datos: cláusula private 

Crea un copia “privada” de la variable para cada hilo  La

variable “privada” no es inicializada y tiene un valor indefinido a la entrada y a la salida int N=1000; int a=1; #pragma omp parallel for private (a) for (int i=0; i< N; i++) { a=a+10; } cout << “El valor de a” << a << endl;

Entorno de datos: Por defecto… 

Todas las variables usadas dentro de un constructor paralelo son tratadas como “compartidas”  Es

el comportamiento deseado generalmente para variables de “sólo lectura” dentro del bucle paralelo

 Si

son modificadas, hay que declararlas privadas o sincronizar el acceso a las mismas

Entorno de datos: Por defecto…(II) Los índices de los bucles paralelos son identificados como tal y declarados privados de forma automática por el compilador; pero…

…los índices de los bucles secuenciales dentro de secciones paralelas en C/C++ siguen siendo compartidos (shared).

Ejemplos

Aquí i es declarada automáticamente como variable privada por el compilador

int N=1000; int i,a=1; int x[1000]; #pragma omp parallel for for (i=0; i< N; i++) { x[i]=i+10; }

Ejemplo 1

int i,a=1,N=1000; Esta i, sin embargo, es compartida por todos los hilos. En este caso tendríamos problemas

#pragma omp parallel { int x[1000]; for (i=0; i< N; i++) { x[i]=i+10; } }

Ejemplo 2

Entorno de datos: reduction 

La reducción de datos es un tipo de cálculo muy frecuente   

Consiste en aplicar repetidamente un operador binario (+,-,x,…) sobre una determinada variable El compilador y el entorno de ejecución implementan este cálculo de la forma más eficiente posible Se usa la cláusula: reduction (oper : lista_var)

Operadores para cláusula reduction +

SUMA

-

RESTA

*

PRODUCTO

int N=1000; int a=1; #pragma omp parallel for reduction ( + : a) for (i=0; i< N; i++) a=+10; cout << “El valor de a es: “ << a << endl;

Sincronización 

OpenMP tiene los siguientes constructores para soportar operaciones de sincronización: 

critical section 



atomic 



Cada thread espera a que todos los threads lleguen

flush 



Es un caso especial del constructor anterior que se usa para ciertas sentencias simples

barrier 



Sólo un thread a la vez puede entrar en una sección crítica

Supone un punto en la ejecución donde un thread intenta crear un visión consistente de la memoria

ordered 

Fuerza a que el bloque se ejecute en orden secuencial

Funciones runtime y variables de entorno 

Rutinas para manejar locks  



Rutinas para modificar/comprobar el número de hilos  





omp_set_nested(), omp_set_dynamic() omp_get_nested, omp_get_dynamic()

¿Estoy en una región paralela? 



omp_set_num_threads(), omp_get_num_threads() omp_get_thread_num(), omp_get_max_threads()

Encender/Apagar anidamiento y modo dinámico 



omp_init_lock(), omp_set_lock() omp_unset_lock(), omp_test_lock()

omp_in_parallel()

¿Cuántos procesadores hay en el sistema? 

omp_num_procs()

Funciones runtime y variables de entorno Variable

OMP_SCHEDULE OMP_NUM_THREADS OMP_DYNAMIC OMP_NESTED

Ejemplo

Descripción

“dynamic,4”

Especifica el tipo de planificación para los bucles paralelos

16

Especifica el número de hilos a usar durante la ejecución

TRUE o FALSE

Habilita/deshabilita el ajuste dinámico de hilos

TRUE o FALSE

Habilita/deshabilita el paralelismo anidado

Variables de entorno

Índice  Introducción: OpenMP a vista de pájaro  Sintaxis básica: Empezando con OpenMP  “Constructores” de OpenMP:  Regiones paralelas  Trabajo compartido (Worksharing)  Entorno de datos  Sincronización  Funciones en tiempo de ejecución y variables de entorno

 Evitando errores de programación SMP  Compiladores OpenMP

Índice  Introducción: OpenMP a vista de pájaro  Sintaxis básica: Empezando con OpenMP  “Constructores” de OpenMP:  Regiones paralelas  Trabajo compartido (Worksharing)  Entorno de datos  Sincronización  Funciones en tiempo de ejecución y variables de entorno

 Evitando errores de programación SMP  Compiladores OpenMP

Compiladores OpenMP

   

Intel C/C++ compiler for Linux Visual Studio 2005 y posteriores GCC versión 4.x SunStudio 12.x

Más información sobre OpenMP 

Bibliografía   

Chandra et al.; Parallel programming in OpenMP; Morgan-Kaufman Chapman et al.; Using OpenMP; MIT Press Quinn, M.; Parallel Programming in C with MPI and OpenMP; McGraw-Hill



Internet •

Matsson, T & Eigenmann, R; “OpenMP: An API for writing portable SMP application software”; Tutorial at Supercomputing ‘99

Related Documents

Openmp
April 2020 2
Openmp-mpi
November 2019 17
Use Openmp Part2
November 2019 2
Use Openmp Part 1
November 2019 2
Use Openmp Part 3
November 2019 6
Use Openmp Part 4
November 2019 3

More Documents from ""