Descargar

Sincronización de los threads y tareas (OpenMP)

Enviado por Pablo Turmero


    edu.red ? Cuando no pueden eliminarse las dependencias de datos entre los threads, es necesario sincronizar su ejecución.

    OpenMP proporciona los mecanismos de sincronización más habituales: exclusión mutua y sincronización por eventos. Sincronización de threads

    edu.red 1. Secciones Críticas Define un trozo de código que no puede ser ejecutado por más de un thread a la vez.

    OpenMP ofrece varias alternativas para la ejecución en exclusión mutua de secciones críticas. Las dos opciones principales son: critical y atomic. Exclusión mútua (SC)

    edu.red ? Directiva critical

    Define una única sección crítica para todo el programa, dado que no utiliza variables de lock. #pragma omp parallel firstprivate(MAXL) { … #pragma omp for for (i=0; iMAXL) MAXL = A[i]; } #pragma omp critical { if (MAXL>MAX) MAX = MAXL; } … } OJO: la sección crítica debe ser lo “menor” posible! Exclusión mútua

    edu.red ? Secciones críticas “específicas” (named) #pragma omp parallel for for (i=0; iMAX) #pragma omp critical(M1) { if (A[i]>MAX) MAX = A[i]; }

    if (A[i] Ejemplo #pragma omp parallel private(nire_it) { omp_set_lock(&C1); mi_it = i; i = i + 1; omp_unset_lock(&C1);

    while (mi_it Ejemplo

    edu.red /* productor */ … dat = …; flag = 1; … ? Sincronización punto a punto La sincronización entre procesos puede hacerse mediante flags (memoria común), siguiendo un modelo de tipo productor / consumidor. /* consumidor */ … while (flag==0) { }; … = dat; … Eventos (flags)

    edu.red Sin embargo, sabemos que el código anterior puede no funcionar correctamente en un sistema paralelo, dependiendo del modelo de consistencia de la máquina.

    Tal vez sea necesario desactivar las optimizaciones del compilador antes del acceso a las variables de sincronización. Eventos (flags)

    edu.red Para asegurar que el modelo de consistencia aplicado es el secuencial, OpenMP ofrece como alternativa la directiva: #pragma omp flush(X) que marca puntos de consistencia en la visión de la memoria (fence). /* productor */ … dat = …; #pragma omp flush(dat) flag = 1; #pragma omp flush(flag) … /* consumidor */ … while (flag==0) { #pragma omp flush(flag) }; #pragma omp flush(dat) … = dat; … Eventos (flags)

    edu.red El modelo de consistencia de OpenMP implica tener que realizar una operación de flush tras escribir y antes de leer cualquier variable compartida. volatile int dat, flag; …

    /* productor */ … dat = …; flag = 1; … volatile int dat, flag; …

    /* consumidor */ … while (flag==0) {}; … = dat; … En C se puede conseguir esto declarando las variables de tipo volatile. Eventos (flags)

    edu.red 4. Secciones “ordenadas”

    #pragma omp ordered

    Junto con la cláusula ordered, impone el orden secuencial original en la ejecución de una parte de código de un for paralelo. #pragma omp paralell for ordered for (i=0; i Ejemplo 1: lista ligada … while (puntero) { (void) ejecutar_tarea(puntero); puntero = puntero->sig; } … Sin la directiva task, habría que contar el número de iteraciones previamente para transformar el while en un for. Tareas

    edu.red puntero = cabecera; #pragma omp parallel { #pragma omp single nowait { while(puntero) { #pragma omp task firstprivate(puntero) { (void) ejecutar_tarea(puntero); } puntero = puntero->sig ; } } } Tareas > Ejemplo 1: lista ligada – openmp

    edu.red long fibonacci(int n) { // f(0)=f(1)=1, f(n) = f(n-1) + f(n-2)

    long f1, f2, fn;

    if ( n == 0 || n == 1 ) return(n);

    f1 = fibonacci(n-1); f2 = fibonacci(n-2);

    fn = f1 + f2;

    return(fn); } Tareas > Ejemplo 2: fibonacci

    edu.red long fibonacci(int n) { long f1, f2, fn;

    if ( n == 0 || n == 1 ) return(n);

    #pragma omp task shared(f1) {f1 = fibonacci(n-1);}

    #pragma omp task shared(f2) {f2 = fibonacci(n-2);}

    #pragma omp taskwait

    fn = f1 + f2;

    return(fn); } Tareas > Ejemplo 2: fibonacci – openmp

    edu.red #pragma omp parallel shared(nth) { #pragma omp single nowait { result = fibonacci(n); } } Posibilidad de aplicar recursividad paralela a partir de un tamaño mínimo de cálculo? Tareas > Ejemplo 2: fibonacci – openmp

    edu.red ? Un par de funciones para “medir tiempos”

    ? omp_get_wtime();

    t1 = omp_get_wtime(); … t2 = omp_get_wtime(); tiempo = t2 – t1;

    ? omp_get_wtick(); precisión del reloj Otras cuestiones

    edu.red ? Programar aplicaciones SMP resulta “más sencillo” que repartir datos por diferentes procesadores y comunicarse por paso de mensajes.

    Pero el uso de variables compartidas por varios threads puede llevar a errores no previstos si no se analiza detenidamente su comportamiento.

    Algunos errores típicos pueden producir carreras (races) en los resultados o dejar bloqueada la ejecución (deadlock). Otras cuestiones

    edu.red ? Carreras Definimos una carrera (race) como la consecución de resultados inesperados e irreproducibles debido a problemas en el acceso y sincronización de variables compartidas. #pragma omp parallel sections { #pragma omp section A = B + C;

    #pragma omp section B = A + C;

    #pragma omp section C = B + A; } !? Otras cuestiones: carreras

    edu.red CONT = 0; #pragma omp parallel sections { #pragma omp section A = B + C; #pragma omp flush (A) CONT = 1; #pragma omp flush (CONT) #pragma omp section { while (CONT<1) { #pragma omp flush (CONT) } B = A + C; #pragma omp flush (B) CONT = 2; #pragma omp flush (CONT) } #pragma omp section { while (CONT<2) { #pragma omp flush (CONT) } C = B + A; } } el contador permite la sincronización entre las secciones (eventos) las operaciones de flush aseguran la consistencia de la memoria. Otras cuestiones: carreras

    edu.red #pragma omp parallel private(tid, X) { tid = omp_get_thread_num();

    #pragma omp for reduction(+:total) nowait for (i=0; i0) omp_unset_lock(&C1); else { … }

    (región paralela con secciones)

    … #pragma omp section { omp_set_lock(&C1); A = A + func1(); omp_set_lock(&C2); B = B * A; omp_unset_lock(&C2); omp_unset_lock(&C1); }

    #pragma omp section { omp_set_lock(&C2); B = B + func2(); omp_set_lock(&C1); A = A * B; omp_unset_lock(&C1); omp_unset_lock(&C2); } … Otras cuestiones: deadlock

    edu.red ? Recomendaciones: ? prestar atención al ámbito de las variables: shared, private, etc.

    ? utilizar con cuidado las funciones de sincronización.

    ? disponer de una versión equivalente secuencial para comparar resultados (serán siempre iguales?). Otras cuestiones: deadlock

    edu.red ? Llamadas en paralelo a funciones de librería ¿habrá problemas con la activación simultánea de más de una instancia de dichas funciones?

    Una librería es thread-safe (re-entrante) si lo anterior no es un problema. Si no es así, habría que utilizar una secuencia tipo:

    LOCK / CALL / UNLOCK Otras cuestiones

    edu.red ? Speed-up El objetivo de programar una aplicación en un sistema paralelo es: – ejecutar el problema más rápido. – ejecutar un problema de mayor tamaño.

    En ambos casos hay que tener en cuenta el overhead añadido al paralelizar el código. Otras cuestiones: speed-up

    edu.red ? Escribir programas paralelos OpenMP es fácil… y también lo es escribir programas de bajo rendimiento.

    ? Principales fuentes de pérdida de eficiencia

    ? el algoritmo en ejecución: Amdahl // desequilibrio de carga ? sincronización: grano muy fino ? comunicación: acceso a variables shared, cache (fallos, falsa compartición…)

    ? implementación de OpenMP / S.O. Limitaciones al rendimiento

    edu.red ? Ejemplos de mejora de la eficiencia: #pragma omp parallel for for (i=0;i