Arquitectura de Computadores: Práctica de Multiprocesadores Introducción a OpenMP Índice 1 Introducción 1. 2. Regiones Paralelas: 3. Distribución del trabajo 4. Gestión de datos 5. Sincronización 6 Funciones 6. F i de d bibli biblioteca t 7. Variables del entorno 8. Ejemplo Π AC : MP: Introd. a OpenMP 2 ¿Qué es OpenMP? API estándar “de facto” para la programación multithread de sistemas de memoria compartida (C, C++ y Fortran) Consta de: ● ● ● Directivas para el compilador Funciones de librería Variables de entorno AC : MP: Introd. a OpenMP 3 Introducción OpenMP se basa en directivas al compilador (¡el código es compilable p incluso si el compilador p no soporta p OpenMP!) p ) Que se aplican a “bloques estructurados”: una o más sentencias con un ppunto de entrada y uno de salida Se apoya en threads, para la paralelización (modelo SPMD) O MP se bbasa en lla paralelización OpenMP l li ió de d bucles b l (datos) (d ) Mismo código fuente para secuencial y paralelo Se protege con el preprocesador: #ifdef _OPENMP …. Los pprototipos p de funciones y los tipos p están en el fichero: #include <omp.h> ¡¡Puede haber interbloqueos q y carreras! También soporta vectorización y coprocesadores AC : MP: Introd. a OpenMP 4 Modelo de programación de OpenMP Modelo de paralelismo: Fork-Join El programa comienza con un solo thread (maestro) El maestro crea threads según necesidad AC : MP: Introd. a OpenMP 5 Ejemplo // Secuencial: void main() { double Res[1000]; for(int i=0;i<1000;i++) do_huge g _comp(Res[i]); p } // Paralelo: void main() { double Res[1000]; #pragma omp parallel for for(int i=0;i<1000;i++) do_huge g _comp(Res[i]); p } Similar la versión paralela y la secuencial. secuencial Pocos cambios, legible y fácil de mantener ACAR: Introducción a OpenMP 6 Creación de threads: Regiones Paralelas Se declaran con la directiva # #pragma omp parallel ll l [cláusulas] [ lá l ] {//código…} Crea N threads que ejecutan en paralelo el mismo código, solo cambia su ID. ¡Al final de la región hay una barrera implícita! El número de threads depende p de variables internas (num_threads, max_threads, dynamic) o de la cláusula c usu num u _t threads(int) eads( t) Si se pone la cláusula if, se paraleliza sólo si se cumple la condición:…parallel if(N>100)… AC : MP: Introd. a OpenMP 7 Regiones Paralelas: Ejemplo #include “omp.h” omp.h Funciones de librería ………… double A[1000]; omp_set_num_threads(4); #pragma omp parallel { int ID = omp_get_thread_num(); omp get thread num(); sumarElemA (ID,A); } Cada thread llama a sumarElemA para ID=0 .. 3 Una sola l copia i de d A, A compartida id por todos d los l threads h d AC : MP: Introd. a OpenMP 8 Regiones Paralelas: Ejemplo #include “omp.h” omp.h ………… double A[1000]; cláusula #pragma omp parallel num num_threads(4) threads(4) { int ID = omp_get_thread_num(); omp get thread num(); sumarElemA (ID,A); } Cada thread llama a sumarElemA para ID=0 .. 3 Una sola l copia i de d A, A compartida id por todos d los l threads h d AC : MP: Introd. a OpenMP 9 Distribución del trabajo: for Se declara con la directiva #pragma omp for [cláusulas] for (;;) {//código…} Divide las iteraciones de un bucle entre los threads di disponibles. ibl Al final hay una barrera implícita, evitable con cláusula nowait Se puede especificar cómo se reparten las iteraciones entre los threads: cláusula schedule AC : MP: Introd. a OpenMP 10 Distribución del trabajo for: Ejemplo 1. Código g secuencial for(i=0;i<N;i++) {a[i] = a[i] + b[i];} 2. Región Paralela OpenMP di ib ió de distribución d trabajo b j manualmente (id, i, Nthrds, istart, iend: son variables privadas, ¿por qué? ¿p q ¿deben ¿ serlo?)) #pragma omp parallel { int id, i,Nthrds,istart,iend; id = omp_get_thread_num(); Nthrds =omp omp_get_num_threads(); get num threads(); istart = id * N / Nthrds; iend = (id+1) * N / Nthrds; for(i=istart;i<iend;i++) ( ) { a[i] = a[i] + b[i];} } 3 3. Región R ió Paralela P l l OpenMP O MP y distribución de trabajo for #pragma omp parallel # #pragma omp for schedule(static) for(i=0;i<N;i++) { a[i] [i] = a[i] [i] + b[i] b[i];} } AC : MP: Introd. a OpenMP 11 Distribución del trabajo for: Planificación Planificación con la cláusula schedule: #pragma omp for schedule(..) schedule (static [,chunk]) Asigna bloques fijos de iteraciones de tamaño “chunk” a cada thread schedule (dynamic [,chunk]) C d thread Cada h d coge “chunk” “ h k” iteraciones i i hasta h terminar i con todas d schedule (guided [,chunk]) ● Cada C d thread th d coge dinámicamente di á i t bloques bl de d iteraciones it i ● El bloque inicialmente es grande (nº iteraciones sin asignar dividido por nº threads) y va bajando hasta tamaño“chunk” schedule (runtime) Planificación y tamaño de bloque determinado por la variable de entorno OMP_SCHEDULE OMP SCHEDULE AC : MP: Introd. a OpenMP 12 Planificación: Ejemplo j p 200 iteraciones en 4 threads AC : MP: Introd. a OpenMP 13 Distribución del trabajo: sections Asigna bloques de código a threads. threads Ejemplo: #pragma omp parallel #pragma omp sections { x_calculation(); #pragma omp section y_calculation(); #pragma omp section z_calculation(); (); } Al final hay una barrera (evitable con nowait) AC : MP: Introd. a OpenMP 14 Gestión de los datos Modelo de programación de memoria compartida: ● ● La mayoría de las variables son compartidas por defecto Las variables globales son compartidas Pero no todas las variables son compartidas: ● Al Algunas ddeben b ser específicas ífi para cada d thread h d ● Las variables declaradas dentro de una región paralela ● Las ubicadas en cada pila propia (parámetros y variables locales) son privadas (cada thread tiene su pila) Se puede cambiar los ‘atributos’ de las variables heredadas del maestro mediante cláusulas AC : MP: Introd. a OpenMP 15 Gestión de los datos shared: Variable común a todos los threads Cuidado: puede ser necesario utilizar regiones críticas private: i t Cada C d thread h d tiene ti “su” “ ” copia i local. l l Sin inicializar y valor indefinido tras la región paralela firstprivate: Es “private”, pero se inicializa con el valor de la ‘original’ (variable del maestro) lastprivate: Es “private”, pero al final se le asigna el valor que toma en la última iteración o sección default: shared, private (FORTRAN) o none AC : MP: Introd. a OpenMP 16 Gestión de los datos threadprivate: hace una copia de una variable, privada para cada d thread. h d Existe i a lo l largo l de d la l ejecución j ió (en ( secuencial se ve la copia del maestro) copyin: inicializa las variables threadprivate con el valor de la del maestro copyprivate: pasa el valor dado por un thread al resto (se usa con single) reduction (op:list): deben ser variables shared • Hará una copia local y la inicializará según “op” • Al final las copias locales se reducen a una global • Operadores: + * - & | ^ && || min max AC : MP: Introd. a OpenMP 17 Gestión de los datos: Ejemplos j p 1. int tmp=0; #pragma omp parallel private(i) firstprivate(tmp) { i = 3 + func(id); tmp = tmp + 2;} 2. #pragma omp parallel for lastprivate(i) for (i=0; i<n-1; i++) a[i] = b[i] + b[i+1 a[i]=b[i]; // caso n-1 fuera bucle 3. double ave=0.0, A[MAX], int i; #pragma omp parallel for \ schedule h d l ( (static)reduction(+:ave) t ti ) d ti ( ) for (i=0; i<MAX; i++) ave+= A[i]; ave = ave/MAX /MAX AC : MP: Introd. a OpenMP 18 Gestión de los datos 1. 2 2. int counter = 0; // Nº tareas ejecutadas por cada hilo… #pragma omp threadprivate(counter) #pragma omp threadprivate(work threadprivate(work,size) size) #pragma omp parallel copyin(size) shared(N) { work = build(tol,size+N); } 3. #pragma omp parallel for reduction(+:res) lastprivate(Z,i) for (i=0; i< 1000; i++){ Z = func(I); res = res + Z; } ACAR: Introducción a OpenMP 19 Sincronización critical [name]: Definición de una región crítica. Los threads esperan al comienzo de la región crítica hasta que no haya ningún hilo ejecutando una región crítica con el mismo nombre. b Todas T d las l que no tienen ti nombre, b se consideran id que tienen ti un mismo nombre no especificado Atomic [read|write|update|capture]: Asegura una actualización atómica. Por defecto update ● ● ● ● Lectura L t atómica: tó i v = x; Escritura atómica: x = expr; //expr no atómica Actualización atómica: x = x binop expr; “Captura” atómica: v = x binop= expr; // x atómico AC : MP: Introd. a OpenMP 20 Sincronización barrier: Implementa una barrera ordered: Asegura que las iteraciones se ejecutan en el mismo orden que en secuencial: ¡¡evítese en lo posible!! master: Sólo el maestro ejecuta dicho código, los demás se lo saltan. Sin barrera. single: g Un solo thread ejecuta j dicho código, g , tiene una barrera implícita. Con copyprivate puede hacer visible a los demás threads los cálculos hechos. AC : MP: Introd. a OpenMP 21 Sincronización: Ejemplos 1. #pragma omp parallel for schedule(dynamic) private(a) for (i=0; i<N; i++){ a = work(i); // en paralelo #pragma omp ordered // espera que le toque printf(“%d\n", printf( %d\n , a); //impresión resultados ordenada } 2. #pragma omp threadprivate(x, y) void init(float a a, float b ) { // a y b privados #pragma omp single copyprivate(a,b,x,y) scanf("%f %f %f %f", &a, &b, &x, &y); } 3. #pragma omp critical qlock enqueue(job); … #pragma omp critical qlock dequeue(job); AC : MP: Introd. a OpenMP 22 Sincronización 1. omp_lock_t _ _ *new_lock() _ { omp_lock_t *lock_ptr; #pragma omp single copyprivate(lock_ptr) { lock_ptr = (omp_lock_t *)malloc(sizeof(omp_lock_t)); omp_init_lock( lock_ptr ); } //barrera, // todos salen con el cerrojo!!! return lock_ptr; } AC : MP: Introd. a OpenMP 23 Tareas A partir de OpenMP 3.0 30 Permite definir una “task”: conjunto de código y datos a ejecutar: j t #pragma omp task firstprivate(my_pointer) (void) do do_independent_work independent work (my (my_pointer); pointer); Si hay algún thread disponible se pone a ejecutarla, si no espera uno libre. Se espera por ellas en las barreras implícitas y explícitas ACAR: Introducción a OpenMP 24 OpenMP 4.0 40 Está disponible OpenMP 4.0 (http://www.openmp.org) Dos cambios importantes: vectorización y coprocesadores Vectorización: nuevo #pragma omp simd para vectorizar bucles. Toma la idea de Intel (icc). Con claúsulas ● Busca aprovechar las unidades vectoriales: MMX, SSE, AVX, MIC) Se podrá combinar con regiones paralelas: MIC). #pragma omp parallel for simd Coprocesadores: Se busca aprovechar las GPUs y MICs, MICs de gran potencia de cálculo. ● Se introduce #pragma omp target para indicar el dispositivo sobre el que se quiere ejecutar ● Toma la idea de PG, que ya dispone del OpenACC, un OpenMP de pago para GPUs ACAR: Introducción a OpenMP 25 Funciones de biblioteca: Entorno de Ejecución Gestión de threads • • • • • • void omp_set_num_threads(int) int t omp o p_get_ get num u _t threads(void) eads( o d) int omp_get_thread_num(void) int omp_get_max_threads(void) _ _ _ void omp_set_dynamic(bool) bool omp_get_dynamic(void) Anidamiento del paralelismo • • void omp_set_nested(bool) omp set nested(bool) bool omp_get_nested(void) AC : MP: Introd. a OpenMP 26 Funciones de biblioteca: Entorno de Ejecución y Cerrojos Cerrojos • • • • • void void void void void oid omp_init_lock(lock) omp_destroy_lock(lock) omp_set_lock(lock) omp_unset_lock(lock) omp_test_lock(lock) omp test lock(lock) ¿En una región paralela? bool omp_in_parallel(void) omp in parallel(void) Número procesadores: int omp omp_num_procs(void) num procs(void) Tomar tiempos omp p_g get_wtime(), omp p_g get_wtick() AC : MP: Introd. a OpenMP 27 Variables del Entorno del Ejecución OMP SCHEDULE “schedule[, OMP_SCHEDULE schedule[, chunk chunk_size] size]” OMP_NUM_THREADS int_literal // Máximo OMP_DYNAMIC bool b l // //Ajusta j el l número ú d de hilos en cada región paralela OMP_NESTED bool OMP_PROC_BIND bool OMP_STACKSIZE int [B|K|M|G} OMP WAIT POLICY passive || active OMP_WAIT_POLICY OMP_THREAD_LIMIT int AC : MP: Introd. a OpenMP 28 Compilación Incluir el fichero de cabecera: #include <omp.h> p Se activa _OPENMP Compilador de GCC: gcc -fopenmp gfortran -fopenmp Compilador de Intel: icc -openmp ifort –openmp AC : MP: Introd. a OpenMP 29 Ejemplo del cálculo de Pi Por cálculo númerico: 1 4.0 dx 2 1 x 0 Se aproxima como: N F ( x )x i 0 i con F(xi) el alto a mitad del intervalo y x el ancho del intervalo AC : MP: Introd. a OpenMP 30 Ejemplo del cálculo de Pi : Secuencial static long num_steps = 100000; double step; void main (){ int i; double x, pi, sum = 0.0; step = 1.0/(double) num_steps; _ for (i=0;i< num_steps; i++){ x = (i+0.5)*step; sum = sum + 4.0/(1.0+x*x); / } pi = step * sum; } AC : MP: Introd. a OpenMP 31
© Copyright 2024