Tareas, dependencias e ICVs
#pragma omp task [untied] [default(shared|none)] [private(list)] [shared(list)] [firstprivate(list)] [if(expr)] [depend(tipo: lista)] bloqueLa tarea se ejecuta de inmediato o se difiere, según haya hilos libres. Patrón típico: un solo hilo crea las tareas (dentro de single) y el equipo entero las ejecuta.
#pragma omp parallel num_threads(2){ #pragma omp single { #pragma omp task printf("t1 : impresion random desde el hilo : %d\n", omp_get_thread_num()); #pragma omp task printf("t2 : impresion random desde el hilo : %d\n", omp_get_thread_num()); #pragma omp task printf("t3 : impresion random desde el hilo : %d\n", omp_get_thread_num()); }}El orden de t1, t2, t3 y el hilo que ejecuta cada una cambian en cada corrida.
Cómo leerlo: el hilo 0, dentro de single, crea tres tareas y las deja en un pool. Los demás hilos las toman cuando están libres. La flecha roja es la dependencia out(x) → in(x): T2 no puede empezar hasta que T1 termine. T3 no tiene depend, así que puede correr en cualquier momento. taskwait hace que el hilo 0 espere a sus tres tareas hijas.
depend: ordenar tareas
Sección titulada «depend: ordenar tareas»| Tipo | Significa | Espera a tareas anteriores que tengan la misma variable en… |
|---|---|---|
in | la tarea lee la variable | out o inout |
out / inout | la tarea escribe (o lee y escribe) | in, out o inout |
Es la misma idea de las dependencias del DAG (U1.3): lectura después de escritura, escritura después de lectura y escritura después de escritura. Dos lecturas no se ordenan entre sí.
Tareas con dependencias: task depend(in/out)
Un hilo (dentro de single) crea las tareas en orden; luego cualquier hilo libre las ejecuta. Las flechas son esperas obligatorias.
int x = 1;
#pragma omp parallel
#pragma omp single
{
#pragma omp task shared(x) depend(out: x)
x = 2;
#pragma omp task shared(x) depend(in: x)
printf("x = %d", x);
}(salida vacía)
taskwait, taskgroup, taskyield
Sección titulada «taskwait, taskgroup, taskyield»taskwait: el hilo espera a que terminen las tareas hijas que creó hasta ese punto.taskgroup { … }: espera a todas las tareas creadas dentro del bloque y sus descendientes. En el ejemplo de U4.3,start_background_work()se crea fuera del grupo, así que sigue corriendo libre mientras cada paso espera solo al recorrido del árbol (compute_tree). Contaskwaitparticiparían todas las tareas hijas.taskyield: la tarea actual cede el hilo para que corra otra tarea. Mejora la concurrencia cuando hay tareas largas que podrían acaparar un hilo.
Variables de control interno (ICVs)
Sección titulada «Variables de control interno (ICVs)»Cada región/tarea recibe una copia de las ICVs, que controlan el comportamiento del programa:
| Función | Controla |
|---|---|
omp_set_num_threads(n) | hilos de la próxima región paralela |
omp_set_dynamic(0/1) | si el runtime puede ajustar el número de hilos |
omp_set_nested(0/1) | permitir regiones paralelas anidadas |
omp_set_max_active_levels(n) | cuántos niveles anidados pueden estar activos |
Consultas útiles: omp_get_num_threads(), omp_get_thread_num(), omp_get_max_threads(), omp_get_max_active_levels(), omp_get_wtime().
omp_set_nested(1);omp_set_max_active_levels(8);omp_set_dynamic(0);omp_set_num_threads(2);#pragma omp parallel // nivel externo: 2 hilos{ omp_set_num_threads(3); #pragma omp parallel // cada uno abre un equipo de 3 { omp_set_num_threads(4); #pragma omp single printf("Inner: ..."); // se imprime 2 veces (una por equipo interno) } #pragma omp barrier #pragma omp single printf("Outer: ..."); // se imprime 1 vez}Grano grueso con parallel solo
Sección titulada «Grano grueso con parallel solo»No siempre hace falta for: cada hilo puede calcular qué parte del arreglo le toca a partir de su id y del número de hilos. Es la misma idea de “n/p datos por proceso” de MPI, pero en memoria compartida.
#pragma omp parallel default(shared) private(iam, nt, ipoints, istart){ iam = omp_get_thread_num(); nt = omp_get_num_threads(); ipoints = npoints / nt; // tamaño del bloque istart = iam * ipoints; // inicio del bloque if (iam == nt - 1) // el último hilo hace lo que sobra ipoints = npoints - istart; subdomain(x, istart, ipoints);}Ejercicio
Sección titulada «Ejercicio»int x = 1;#pragma omp parallel#pragma omp single{ #pragma omp task shared(x) depend(out: x) x = 2; #pragma omp task shared(x) depend(in: x) printf("x+1 = %d. ", x + 1); #pragma omp task shared(x) depend(in: x) printf("x+2 = %d\n", x + 2);}¿Cuáles son las salidas posibles? ¿Puede salir x+1 = 2?
Ver solución
Las dos tareas que imprimen dependen de la primera (in después de out), así que siempre ven x = 2. Entre ellas no hay dependencia (dos in), por lo que el orden es libre:
x+1 = 3. x+2 = 4x+2 = 4y luegox+1 = 3.
x+1 = 2 no puede salir: requeriría leer x antes de que la primera tarea la cambie.
Errores típicos
Sección titulada «Errores típicos»- Crear las tareas fuera de
single: cada hilo del equipo las crea, y se ejecutan p veces. - Olvidar que las variables locales del creador son, por defecto,
firstprivateen la tarea: si la tarea las modifica, el cambio no se ve fuera. Usasharedy luegotaskwait. - Usar el resultado de una tarea sin
taskwait(otaskgroup) antes. - Esperar que
dependordene tareas de padres distintos: solo ordena tareas hermanas (creadas por la misma tarea).
Práctica extra
Sección titulada «Práctica extra»Se crean en este orden: T1 depend(out: a), T2 depend(in: a), T3 depend(out: a), T4 depend(in: b). ¿Qué órdenes son posibles?
Ver solución
- T1 → T2 (T2 lee lo que T1 escribe).
- T2 → T3 (T3 no puede escribir
aantes de que T2 lo lea). - T1 → T3 (dos escrituras de
aen orden). - T4 no comparte variables con nadie: va en cualquier lugar.
Así que T1, T2, T3 siempre en ese orden, y T4 en cualquiera de las 4 posiciones: 4 órdenes posibles (T4 T1 T2 T3, T1 T4 T2 T3, T1 T2 T4 T3, T1 T2 T3 T4), además de que T4 puede correr a la vez que cualquiera.
Autoevaluación
0/5 correctas · 0 respondidasdepend(in: x) espera a tareas anteriores con…in: x (printf) y luego out: x (x = 2), con x = 1 al inicio. Se imprime…taskwait espera a…omp_set_nested(1), un parallel dentro de otro parallel usa…single?Fuente ·U4.3 Paralelismo compartido-OMP.pdf (ICVs, parallel, task, depend, taskgroup, taskyield)