OpenMP
OpenMP es una interfaz de programación de aplicaciones (API) para la programación
multiproceso de memoria compartida en múltiples plataformas. Permite añadir
concurrencia a los programas escritos en C, C++ y Fortran sobre la base del modelo de
ejecución fork-join. Está disponible en muchas arquitecturas, incluidas las plataformas de
Unix y de Microsoft Windows. Se compone de un conjunto de directivas de compilador,
rutinas de biblioteca, y variables de entorno que influyen el comportamiento en tiempo de
ejecución.
Definido conjuntamente por proveedores de hardware y de software, OpenMP es un
modelo de programación portable y escalable que proporciona a los programadores una
interfaz simple y flexible para el desarrollo de aplicaciones paralelas, para plataformas que
van desde las computadoras de escritorio hasta supercomputadoras. Una aplicación
construida con un modelo de programación paralela híbrido se puede ejecutar en un
cluster de computadoras utilizando OpenMP y MPI, o a través de las extensiones de
OpenMP para los sistemas de memoria distribuida.
Sintaxis básica
La sintaxis básica que nos encontramos en una directiva de OpenMP es para C/C++:
# pragma omp <directiva> [cláusula [ , ...] ...]
para Fortran:
!$OMP PARALLEL <directiva> [cláusulas]
[ bloque de código ]
!$OMP END <directiva>
Directivas
También se suelen llamar constructores:
• parallel : Esta directiva nos indica que la parte de código que la comprende puede
ser ejecutada por varios hilos. Se crea una tarea implícita para cada hilo perteneciente
al equipo de hilos creado por el parallel .
En este fragmento de código se trata de explotar el potencial del paralelismo en una tarea de
una serie geométrica que tiende al infinito para la ecuación:
/* Juan Carlos Lanuza Lazo
MGA-NI 27 / Apr / 2017
Ejemplo de sumatoria paralela de una serie Armonica
*/
#include <omp.h>
#include <stdio.h>
#define LIMIT 1000000000000
int main(void)
{
double serie = 0;
long long i;
#pragma omp parallel
{
#pragma omp for reduction(+:serie)
for(i = 1;i <= LIMIT;i++)
serie += 1.0/i;
}
printf("Valor de la Serie: %.5f\n",serie);
return 0;
}
• for : El equipo de hilos que se encuentra con el for ejecuta una o más fracciones
de iteraciones como resultado de dividir el bucle delimitado por la directiva entre los
hilos del equipo (el tamaño de cada partición dependerá de las cláusulas opcionales
añadidas al for ). Su formato es:
#pragma omp parallel for [cláusula, ... , cláusula]
Cada fracción de iteraciones es una tarea implícita (el programador no ha particionado el
bucle manualmente y anotado una tarea por cada trozo).
• section y sections: Indica secciones que pueden ejecutarse en paralelo, pero por
un único hilo.
• single : La parte de código que define esta directiva, solo se puede ejecutar un
único hilo de todos los lanzados, y no tiene que ser obligatoriamente el hilo padre.
• master : La parte de código definida, solo se puede ejecutar por el hilo padre.
• critical : Solo un hilo puede estar en esta sección. Definen secciones
críticas o condiciones de carrera.
• atomic : Se utiliza cuando la operación atañe a sólo una posición de memoria, y tiene
que ser actualizada solo por un hilo simultáneamente. Operaciones tipo x++ o --x son
las que usan esta cláusula.
• flush : Esta directiva resuelve la consistencia, al exportar a todos los hilos un valor
modificado de una variable que ha realizado otro hilo en el procesamiento paralelo.
• barrier : Crea una barrera explícita en la que los hilos se quedan esperando hasta
que todo el equipo la haya alcanzado.
• task : Permite la ejecución asíncrona mediante tareas explícitas. El bloque de código
dentro de la directiva task es ejecutado por un único hilo, y la tarea es instanciada
por el hilo que se encuentra con la directiva. Por ello es recomendable utilizar esta
directiva dentro de una directiva single , evitando crear (y ejecutar) la misma tarea
tantas veces como hilos haya. Su formato es: #pragma omp task [cláusulas] .
Cuando la tarea se instancia ésta es insertada en una cola de pendientes. Las tareas
de la cola de pendientes son seleccionadas por la implementación de OpenMP en
función de sus dependencias cumplidas y prioridad. Las tareas seleccionadas pasan a
la cola de listas para ejecutar. Los hilos del equipo activo cogen tareas de dicha cola y
las ejecutan. Uso de task con single :
#define NUM_ITERS 1000000
#pragma omp parallel
{
int32_t BS = NUM_ITERS/omp_get_num_threads();
#pragma omp single
{
for (int i = 0; i < NUM_ITERS; i+= BS) {
int32_t upper = i+((NUM_ITERS-BS >= 0) ? BS : NUM_ITERS-BS);
#pragma omp task firstprivate(i,upper)
{
for (int ii = i; ii < upper; ii++)
a[ii] += b[ii]*c[ii];
}
}
#pragma omp taskwait
}
}
Funciones
La API de OpenMP dispone de una serie de funciones para obtener información y
configurar el entorno paralelo. También permite manejar candados (locks) y tomar
mediciones de tiempo. Además de las funciones aquí mostradas, cada implementación de
OpenMP tiene funciones y variables de entorno propias. Por ejemplo, el OpenMP de GNU
(GOMP, GNU OpenMP) dispone de variables de entorno propias.1 Hay partes de la
especificación que son dependientes de la implementación por lo que el programa no
tiene porqué comportarse igual si el binario se genera primero con una implementación y
luego por otra. Por ejemplo, la creación de regiones paralelas anidadas no especifica
cómo se han de suministrar los hilos para las regiones anidadas. En el caso de GNU
OpenMP mantiene un único equipo de hilos y lo hace crecer o decrecer en función de la
cantidad de hilos demandada por la región paralela activa. Esto es una optimización que
ayuda a mantener la localidad de datos y disminuir los sobrecostes de creación y
destrucción de hilos. Sin embargo, una implementación podría no mantener un equipo
único y crear nuevos equipos enteros. Esto provoca la expulsión de los hilos de regiones
paralelas predecesoras. La asignación de identificadores a procesadores no sigue
siempre el mismo orden salvo que se indique lo contrario. Si el código del programa
depende del identificador del hilo para la asignación de datos a procesar, se puede perder
la localidad espacial del programa. Crear y destruir múltiples equipos de hilos requiere
también de más tiempo que reutilizar equipos de hilos ya existentes.
Funciones para controlar hilos, procesadores y el entorno
paralelo
Estas funciones se enlazan según las normas del lenguaje C y no lanzan excepciones.
• omp_get_active_level() : Regiones paralelas activas en el momento de invocar la
función.
• omp_get_ancestor_thread_num(int level) : Devuelve el TID (Thread ID) en el nivel
de anidamiento de paralelismo en cuestión que se corresponde con el hilo que invoca la
función.
• omp_get_cancellation() : ¿Soporta cancelación la implementación de OpenMP en
uso? Controlado por la variable de entorno OMP_CANCELLATION .
• omp_get_default_device() : Obtener el dispositivo por defecto en las
regiones target que no tienen cláusula device .
• omp_get_dynamic() : ¿Está activa la funcionalidad de creación de equipos de tamaño
dinámico? Valor controlado por variable de
entorno OMP_DYNAMIC o omp_set_dynamic() .
Funciones para inicializar, activar, desactivar, comprobar y
destruir candados simples y anidados
• omp_init_lock(omp_lock_t *lock) : Inicializar un candado.
• omp_set_lock(omp_lock_t *lock) : Esperar a que el candado esté inactivo y activarlo
para el hilo invocante.
• omp_test_lock(omp_lock_t *lock) : Comprobar si el candado está inactivo y
activarlo para el hilo invocante si la comprobación es positiva.
• omp_unset_lock(omp_lock_t *lock) : Desactivar un candado simple.
Funciones para medir el tiempo
• omp_get_wtick : Obtener la precisión del cronómetro: cuántos segundos pasan entre dos
ticks de reloj.
• omp_get_wtime : Obtener el tiempo del cronómetro. El tiempo se corresponde con el real,
es decir, tiempo de ejecución de programa, no tiempo de proceso. El tiempo de proceso
sería la suma de los tiempos de actividad de todos los hilos. La unidad de medida son los
segundos. No hay garantía de que dos hilos distintos den el mismo tiempo.
Variables de entorno
• OMP_CANCELLATION : Configura la posibilidad de utilizar la directiva #pragma omp
cancel
• OMP_DISPLAY_ENV : Muestra la versión de OpenMP y las variables de entorno.
• OMP_DEFAULT_DEVICE : Configura el dispositivo utilizado en las regiones target
• OMP_DYNAMIC : Activa el ajuste dinámico de hilos.
• OMP_MAX_ACTIVE_LEVELS : Configura la máxima cantidad de regiones paralelas
anidadas.
• OMP_MAX_TASK_PRIORITY : Configura la prioridad máxima de una task .
• OMP_NESTED : Activa el uso de regiones paralelas anidadas. Si TRUE entonces los
miembros de un equipo de hilos pueden crear nuevos equipos de hilos.
• OMP_NUM_THREADS : Fija el número de hilos simultáneos.
Webgrafías:
[Link]