OpenMP — memoria compartida y fork-join
¿Qué es OpenMP?
Sección titulada «¿Qué es OpenMP?»OpenMP (Open Multi-Processing) es una API para programar en paralelo con memoria compartida, en C/C++ y Fortran. Tiene tres piezas:
| Pieza | Para qué | Ejemplo |
|---|---|---|
Directivas de compilador (#pragma omp …) | Abrir regiones paralelas, repartir bucles, sincronizar | #pragma omp parallel for |
Librería (omp.h) | Preguntar o fijar número e id de hilos, medir tiempo | omp_get_thread_num(), omp_get_wtime() |
| Variables de entorno | Controlar la ejecución sin recompilar | OMP_NUM_THREADS=4, OMP_SCHEDULE |
Las directivas son “comentarios con significado”: si compilas sin -fopenmp, el compilador las ignora y el programa corre secuencial. Por eso el código del curso protege las llamadas a la librería con #ifdef _OPENMP.
- Dónde corre: una sola máquina con memoria compartida (UMA o ccNUMA): un socket/CPU o un nodo con varios sockets. No sirve entre nodos de un clúster (eso es memoria distribuida → MPI).
- Hilo (thread): la unidad de ejecución más pequeña que el sistema operativo puede planificar. Tú decides cuántos.
Modelo fork-join
Sección titulada «Modelo fork-join»- El programa arranca con un solo hilo: el hilo maestro (master, id 0).
- FORK: al encontrar
#pragma omp parallel, el maestro crea un equipo de hilos que ejecutan el bloque. - JOIN: al cerrar el bloque hay una barrera implícita: todos esperan al más lento, el equipo desaparece y solo sigue el maestro.
OpenMP: modelo fork-join y la primera condición de carrera
OpenMP vs MPI
Sección titulada «OpenMP vs MPI»| OpenMP | MPI | |
|---|---|---|
| Memoria | compartida (una máquina) | distribuida (muchos nodos) |
| Unidad | hilos | procesos |
| Comunicación | leer/escribir variables compartidas | mensajes (Send/Recv, colectivas) |
| Esfuerzo para empezar | bajo: agregar #pragma a un código que ya funciona | alto: reescribir el programa |
| Techo de rendimiento | limitado por los núcleos de un nodo | escala a todo el clúster |
| Riesgo típico | condiciones de carrera | deadlocks |
Cómo leerlo: a la izquierda los 4 hilos tienen cada uno su pila (ahí viven las variables privadas, como id o el índice i), pero todos apuntan a la misma memoria: si dos escriben a la vez la misma variable hay carrera. A la derecha cada proceso MPI tiene su propia memoria; para que otro vea un dato hay que mandarlo en un mensaje.
La lámina “OMP vs MPI” lo resume con una curva rendimiento vs esfuerzo: OpenMP sube rápido pero se aplana (se acaban los núcleos). MPI cuesta más al inicio (“el código no funciona” durante un tramo) pero llega más alto, y OpenMP + MPI (programación híbrida) llega todavía más arriba.
Compilar y ejecutar
Sección titulada «Compilar y ejecutar»g++ -fopenmp Ejemp01_hello_omp.cpp -o hello # sin -fopenmp, los #pragma se ignoranOMP_NUM_THREADS=4 ./hello # o omp_set_num_threads(4) en el código#include <iostream>#include <unistd.h>#ifdef _OPENMP#include <omp.h>#endif
int main() { int id = -1, x = 10;#pragma omp parallel shared(x) private(id) {#ifdef _OPENMP id = omp_get_thread_num(); // 0, 1, 2, ... en cada hilo#endif sleep(1); // fuerza la carrera si id fuera shared printf("Peekaboo desde el hilo = %d \n", id); }}Con 4 hilos imprime 4 líneas Peekaboo desde el hilo = 0…3 en un orden distinto cada vez: los hilos no tienen orden garantizado.
Ejercicio
Sección titulada «Ejercicio»¿Qué cambia si quitas private(id) y dejas el sleep(1)? ¿Y si además quitas el sleep?
Ver solución
Sin private(id), id es compartida (por defecto las variables declaradas fuera de la región son shared). Con el sleep(1), todos los hilos escriben su número casi a la vez, duermen 1 s y luego leen: todos imprimen el valor del último que escribió (por ejemplo, cuatro veces = 3). Sin el sleep la carrera sigue existiendo, pero es más difícil de ver: a veces sale bien y a veces no. Que funcione una vez no prueba que sea correcto.
Errores típicos
Sección titulada «Errores típicos»- Creer que
#pragma omp parallelreparte el trabajo. Solo crea el equipo: todos los hilos ejecutan el bloque completo. Para repartir las iteraciones hace faltafor(oparallel for). - Olvidar
-fopenmp: compila sin error y corre con un hilo. Si el speedup es exactamente 1, revisa esto primero. - Esperar que los hilos impriman en orden. No hay orden garantizado entre hilos.
- Querer usar OpenMP entre nodos de un clúster: ahí no hay memoria compartida, hace falta MPI.
Práctica extra
Sección titulada «Práctica extra»Con este código, ¿cuántas veces sale cada letra y qué orden está garantizado?
printf("A\n");#pragma omp parallel num_threads(3){ printf("B\n"); #pragma omp single printf("C\n");}printf("D\n");Ver solución
A 1 vez (antes del fork, solo el maestro), B 3 veces (cada hilo), C 1 vez (single: un hilo cualquiera), D 1 vez (después del join).
Garantizado: A es la primera y D la última (el join es una barrera). Las tres B y la C se mezclan, con una sola regla: la C sale después de la B del hilo que la imprime.
Autoevaluación
0/5 correctas · 0 respondidas#pragma omp parallel ocurre…-fopenmp…Fuente ·U4.1 Paralelismo compartido-OMP.pdf (láminas 2–12) · semana_9/Ejemp01_hello_omp.cpp