Alcance de datos — shared, private, firstprivate, lastprivate, threadprivate, reduction
Reglas por defecto
Sección titulada «Reglas por defecto»- Variables declaradas antes de la región paralela → shared.
- Variables declaradas dentro del bloque → private de cada hilo.
- El índice del bucle de un
#pragma omp for→ private automáticamente. default(shared | none): cambia el comportamiento por defecto. Condefault(none)el compilador te obliga a declarar el alcance de cada variable (buena práctica para no olvidar ninguna).
¿Qué x ve cada hilo? Cláusulas de datos
💡 Cada uno recibe una fotocopia del cuaderno original; al salir la bota.
int x = 5;
#pragma omp parallel firstprivate(x)
{
x += omp_get_thread_num() + 1; // cada copia empieza en 5
}
printf("%d", x);Tabla resumen
Sección titulada «Tabla resumen»| Cláusula | ¿Copia por hilo? | Valor inicial de la copia | ¿Y al salir? |
|---|---|---|---|
shared | no, una sola | — | lo que haya quedado (¡cuidado con las carreras!) |
private | sí | sin inicializar | el original no cambia |
firstprivate | sí | el valor original | el original no cambia |
lastprivate | sí | sin inicializar | original = valor de la última iteración/sección secuencial |
reduction(op: x) | sí | el neutro de op | original = original op (todas las copias) |
threadprivate (directiva, para globales) | sí, persistente | se inicializa una vez | cada hilo conserva su valor en la siguiente región |
copyin | — | copia el valor del maestro a las threadprivate de todos | — |
copyprivate (con single) | — | difunde el valor del hilo del single a las privadas de los demás | — |
Restricción de private: un elemento que es parte de otra variable (un elemento de arreglo o un campo de struct) no puede ir en la lista.
Cómo leerlo: cada columna es una cláusula y cada círculo de color, un hilo con su copia. Con shared hay una caja para todos. Con private cada hilo recibe una copia sin inicializar (rojo). firstprivate copia el 10 del maestro. lastprivate devuelve al final el valor de la última iteración del orden secuencial. reduction empieza cada copia en el neutro (0 para +) y al final suma todo más el valor original.
Salidas reales (4 hilos)
Sección titulada «Salidas reales (4 hilos)»private / firstprivate (Ejemp02_private.c)
Sección titulada «private / firstprivate (Ejemp02_private.c)»i = 1; j = 2;//#pragma omp parallel private(i,j)#pragma omp parallel firstprivate(i,j){ i = 3; j = j + 2; printf("parallel i: %i j: %i \n", i, j);}printf("i: %i j: %i \n", i, j);parallel i: 3 j: 4 ← ×4 (cada copia de j empezó en 2)i: 1 j: 2 ← los originales no cambiaronCon private(i,j) (la línea comentada), j = j + 2 usa una j sin inicializar: el valor dentro es basura. Afuera igual se imprime i: 1 j: 2.
lastprivate (Ejemp02_lastprivate.c)
Sección titulada «lastprivate (Ejemp02_lastprivate.c)»int i = 5;#pragma omp parallel{ #pragma omp for lastprivate(i) for (i = 0; i < n-1; i++) a[i] = b[i] + b[i+1];}a[i] = b[i]; /* i == n-1 aquí, como en la versión secuencial */Con n = 10 y b[i] = 2i: a[0..8] = 2, 6, 10, …, 34 y a[9] = b[9] = 18. Sin lastprivate, después del bucle i valdría 5 (el original) y se escribiría a[5] en vez de a[9].
threadprivate (Ejemp02_threadprivate.c)
Sección titulada «threadprivate (Ejemp02_threadprivate.c)»int a = 0, b = 0, i, tid;float x;#pragma omp threadprivate(x)...omp_set_dynamic(0); // necesario para que x persista#pragma omp parallel private(b, tid){ tid = omp_get_thread_num(); a = tid; b = tid; x = 1.1 * tid + 1.0; ... }#pragma omp parallel private(tid){ tid = omp_get_thread_num(); printf("Thread %d: a,b,x= %d %d %f\n", tid, a, b, x); }1st Parallel Region: (ordenado aquí; el orden de las líneas varía)Thread 0: a,b,x= 0 0 1.000000Thread 1: a,b,x= 1 1 2.100000Thread 2: a,b,x= 2 2 3.200000Thread 3: a,b,x= 3 3 4.3000002nd Parallel Region:Thread 0: a,b,x= 3 0 1.000000 ← x se conservó por hiloThread 1: a,b,x= 3 0 2.100000Thread 2: a,b,x= 3 0 3.200000Thread 3: a,b,x= 3 0 4.300000x(threadprivate) se conserva en cada hilo entre regiones.bera private en la región 1: la global nunca cambió →0.aera shared: los 4 hilos la escribieron a la vez y quedó el valor del último (aquí3; la lámina muestra1). Es una carrera: no se puede predecir.
reduction
Sección titulada «reduction»Cada hilo recibe una copia privada inicializada con el neutro del operador; al final se combinan las copias con el operador y se aplican al original.
| Operador | Valor inicial |
|---|---|
+, -, |, ^, || | 0 |
*, && | 1 |
& | ~0 (todos los bits en 1) |
max | el menor valor representable del tipo |
min | el mayor valor representable del tipo |
#pragma omp parallel for default(shared) private(i) schedule(static, chunk) reduction(+:result)for (i = 0; i < n; i++) result += a[i] * b[i];Ejercicio
Sección titulada «Ejercicio»int x = 10;#pragma omp parallel num_threads(3) firstprivate(x){ x += omp_get_thread_num(); printf("%d ", x); }printf("| %d\n", x);¿Qué imprime? ¿Y si cambias firstprivate por reduction(+:x)?
Ver solución
Con firstprivate: cada copia empieza en 10 ⇒ imprime 10, 11 y 12 en algún orden, y luego | 10 (el original no cambia).
Con reduction(+:x): cada copia empieza en 0 ⇒ imprime 0, 1 y 2 en algún orden; al final x = 10 + (0 + 1 + 2) ⇒ | 13.
Errores típicos
Sección titulada «Errores típicos»- Suponer que
privatecopia el valor original: la copia empieza con basura. Para eso estáfirstprivate. - Declarar una variable temporal fuera de la región y usarla dentro sin
private: es compartida y hay carrera. - Olvidar que en
reductionel valor original participa: sis = 5antes, el resultado es 5 + la suma. - Creer que
lastprivatedeja el valor del último hilo en terminar. Deja el de la última iteración secuencial (la de índice mayor).
Práctica extra
Sección titulada «Práctica extra»int x = 10, s = 5;#pragma omp parallel for num_threads(2) schedule(static) firstprivate(x) lastprivate(x) reduction(+:s)for (int i = 0; i < 4; i++) { x += i; s += i; }¿Cuánto valen x y s al final? ¿Y si se usan 4 hilos?
Ver solución
Con 2 hilos: el hilo 0 hace i = 0, 1 → su x = 10 + 0 + 1 = 11. El hilo 1 hace i = 2, 3 → su x = 10 + 2 + 3 = 15. La última iteración (i = 3) es del hilo 1, así que x = 15. s = 5 + (0 + 1 + 2 + 3) = 11.
Con 4 hilos cada uno hace una iteración: el hilo 3 hace i = 3 → x = 10 + 3 = 13. s sigue siendo 11.
Moraleja: con firstprivate + lastprivate el resultado depende del reparto. reduction no.
Autoevaluación
0/6 correctas · 0 respondidasprivate al entrar a la región vale…firstprivate(x) con x = 5: dentro, cada hilo empieza con…for lastprivate(i) de 0 a n−1, el original i vale…reduction(*:p):threadprivate se usa para…#pragma omp for es…Fuente ·U4.1 Paralelismo compartido-OMP.pdf (láminas 29–35) · U4.2 Paralelismo compartido-OMP.pdf (private, lastprivate, threadprivate, reduction) · semana_9/sesionb/Ejemp02_private.c · semana_9/sesionb/Ejemp02_lastprivate.c · semana_9/sesionb/Ejemp02_threadprivate.c · semana_9/Ejemp06_reduction.cpp