Ir al contenido

sections, single, master y barreras

Cada section la ejecuta un hilo, una sola vez; secciones distintas pueden ir en hilos distintos. Es paralelismo funcional (distintas tareas), a diferencia de for (mismo trabajo sobre datos distintos). Hay barrera implícita al final, salvo nowait.

Ejemp03_sections_omp.cpp
#pragma omp parallel num_threads(4) shared(a, b, c, d) private(i, id)
{
// ¿se puede usar nowait?
#pragma omp sections nowait
{
#pragma omp section
{
for (i = 0; i < N; i++) {
c[i] = a[i] + b[i];
printf("seccion 1. Hilo: %i it: %i \n ", omp_get_thread_num(), i);
}
}
#pragma omp section
{
for (i = 0; i < N; i++) {
d[i] = a[i] * b[i];
printf("seccion 2. Hilo: %i it: %i \n ", omp_get_thread_num(), i);
}
}
}
}

Con 4 hilos, solo 2 trabajan (hay 2 secciones); los otros 2 no tienen nada que hacer. Cada sección completa la hace un único hilo (en una corrida: la sección 1 fue del hilo 1, de it: 0 a it: 19).

singlemaster
¿Quién lo ejecuta?cualquier hilo (el primero que llega)solo el hilo 0
¿Los demás esperan al final?sí (barrera implícita), salvo nowaitno
¿Acepta cláusulas?private, firstprivate, copyprivate, nowaitninguna
Uso típicoE/S, inicializar algo compartidoreportes de avance
Ejemp04_single_omp.cpp
#pragma omp parallel
{
#pragma omp single nowait
{
a = 10;
id = omp_get_thread_num();
b[id] = a;
cout << "constructor Single ejecutado por el hilo " << id << endl;
}
}

Salida real (4 hilos): constructor Single ejecutado por el hilo 3 y luego solo b[3] = 10; el resto de b sigue en -1. En otra corrida puede ser otro hilo: no se sabe cuál entra a un single.

#pragma omp barrier: cada hilo que llega espera a todos los del equipo. Además hay barreras implícitas (y con ellas un flush) al final de:

  • parallel (siempre, es el join),
  • for, sections, single (salvo nowait).

No hay barrera al final de master ni de critical.

hilo 0foresperasingletrabajohilo 1foresperatrabajohilo 2foresperaesperatrabajobarrera del forbarrera del singlemaster: sin barrerarojo claro = esperando a los demás · los hilos 1 y 2 no esperan al master del hilo 0

Cómo leerlo: el tiempo avanza a la derecha. Los tres hilos hacen su parte del for; los que terminan antes esperan (rojo claro) en la barrera implícita del for. Luego un solo hilo (el 0 en este ejemplo, podría ser cualquiera) ejecuta el single y los otros esperan en su barrera. El master lo hace solo el hilo 0 y no tiene barrera: los hilos 1 y 2 siguen de largo.

¿Quién espera a quién? Barreras implícitas

Arma el cuerpo de un #pragma omp parallel y mira dónde se frenan los hilos. Rojo = esperando.

1.
2.
3.
hilo 0hilo 1hilo 2hilo 3
tiempo de la región10.1
tiempo perdido esperando (todos)13.2
barreras4
  • for: barrera implícita al final (quítala con nowait).
  • single: un hilo trabaja; los demás esperan al final (salvo nowait).
  • El cierre de parallel siempre tiene barrera (es el join).

Ejemp01_master.c: single + reduction + master en un bucle

Sección titulada «Ejemp01_master.c: single + reduction + master en un bucle»
sesionb/Ejemp01_master.c
#pragma omp parallel num_threads(4)
{
do {
#pragma omp for
for (i = 1; i < n-1; ++i) xold[i] = x[i]; // barrera al final
#pragma omp single
{ toobig = 0; } // uno reinicia; barrera al final
#pragma omp for private(y, error) reduction(+:toobig)
for (i = 1; i < n-1; ++i) {
y = x[i];
x[i] = average(xold[i-1], x[i], xold[i+1]); // suavizado (como el PD01)
error = y - x[i];
if (error > tol || error < -tol) ++toobig;
} // barrera: toobig ya sumado
#pragma omp master
++c; // solo el hilo 0 cuenta vueltas
} while (toobig > 0);
}

Salida real (n = 512, tol = 0.01): iteration 12385, toobig=0.

  • single reinicia toobig una vez y su barrera garantiza que nadie empieza a sumar antes.
  • reduction(+:toobig) cuenta en paralelo sin carrera.
  • master incrementa c sin barrera: solo el hilo 0 lo hace.

En Ejemp01_master.c, ¿qué pasaría si toobig = 0 se hiciera con master en vez de single?

Ver solución

master no tiene barrera: otros hilos podrían empezar el siguiente for con reduction(+:toobig) antes de que el hilo 0 ponga toobig = 0. La lámina de U4.2 sobre reduction muestra exactamente ese error: la inicialización tiene que quedar antes de la reducción, con una barrier después del master, usando single (que trae barrera) o inicializando antes de la región paralela.

  • Creer que master tiene barrera al final. No la tiene; single sí (salvo nowait).
  • Decir que single lo ejecuta el hilo 0. Lo ejecuta el primero que llega; el que siempre es el 0 es master.
  • Poner un barrier dentro de un if (id == 0), un single o un master: no todos los hilos llegan y el programa se cuelga (o el comportamiento no está definido).
  • Esperar que con 2 hilos y 3 section corran las 3 a la vez: un hilo hace dos, una detrás de otra.

Tres section que tardan 2 s, 1 s y 1 s. ¿Cuánto tarda el bloque sections con 1, 2 y 3 hilos? ¿Cuál es el speedup máximo?

Ver solución
  • 1 hilo: 2 + 1 + 1 = 4 s.
  • 2 hilos: el mejor reparto es un hilo con la de 2 s y el otro con las dos de 1 s → 2 s.
  • 3 hilos: una sección por hilo → 2 s (manda la más larga).

Smáx = 4/2 = 2, aunque haya 3 hilos: el tiempo nunca baja de la sección más larga. Es el mismo argumento del camino crítico de un DAG.

Autoevaluación

0/5 correctas · 0 respondidas
1. ¿Qué constructor NO tiene barrera implícita al final?
2. 3 secciones y 8 hilos: ¿cuántos hilos trabajan dentro de sections?
3. single lo ejecuta…
4. Para que los demás no esperen al terminar un single se usa…
5. sections es paralelismo…

Fuente ·U4.1 Paralelismo compartido-OMP.pdf (láminas 18–22, 25) · U4.2 Paralelismo compartido-OMP.pdf (master, barrier) · U4.3 Paralelismo compartido-OMP.pdf (sections, single) · semana_9/Ejemp03_sections_omp.cpp · semana_9/Ejemp04_single_omp.cpp · semana_9/sesionb/Ejemp01_master.c