Ir al contenido

Bloqueante vs no bloqueante

  • Mensaje menor que un umbral (KB–MB, eager): MPI_Send copia el mensaje a un buffer y el emisor sigue; se entrega cuando el destino llama a MPI_Recv. Menos overhead para mensajes chicos.
  • Mensaje mayor que el umbral (rendezvous): MPI_Send funciona como síncrono (espera al receptor), para no llenar el buffer.

En un anillo (todos Send al vecino derecho y luego Recv del izquierdo): con mensajes grandes o Ssend ⇒ BLOQUEO MUTUO; si un proceso rompe el ciclo ⇒ SECUENCIAL.

Tres pasos: (1) iniciar (I = retorno inmediato) → (2) hacer cómputo u otra comunicación → (3) esperar a que se complete (Wait/Test).

MPI_Isend(buf, count, type, dest, tag, comm, &request)
MPI_Irecv(buf, count, type, src, tag, comm, &request)
MPI_Wait(&request, &status) // bloquea hasta completar
MPI_Test(&request, &flag, &status) // solo consulta (flag = true/false)
MPI_Waitall / Waitany / Waitsome // varias requests

Prueba en orden: Send → Recv con mensaje grande (deadlock) y pequeño (funciona); Ssend → Recv (siempre deadlock); el maestro rompe el ciclo (secuencial: mira la escalera en la línea de tiempo); pares/impares (dos fases); Irecv + Isend (sin bloqueo); y el DAG del PD03.

Simulador de mensajes MPI

Simulación en JS (no ejecuta MPI real). Cada tic, cada proceso intenta avanzar una instrucción. Las insignias P0…P3 marcan la línea donde está cada proceso.

session3/ejemplo01a_bloqueada.cpp · N = 1<<17 enteros (512 KB): supera el umbral eager ⇒ MPI_Send se comporta como síncrono.
t = 0
1prev = rank - 1;
2next = rank + 1;
3if (rank == 0) prev = numtasks - 1;
4if (rank == (numtasks - 1)) next = 0;P0P1P2P3
5
6// COMUNICACION CICLICA
7// 1. Cada proceso envía el buffer a su vecino derecho
8// y recibe de su vecino izquierdo
9MPI_Send(sbuf, N, MPI_INT, next, tag1, MPI_COMM_WORLD);
10MPI_Recv(rbuf, N, MPI_INT, prev, tag1, MPI_COMM_WORLD, &status);
Línea de tiempo
P0P1P2P312345678
avanza (color del proceso)bloqueadoterminado
Registro de mensajes…
Con mensajes pequeños (eager) MPI_Send copia al buffer del sistema y retorna: no hay bloqueo. Con mensajes grandes todos quedan esperando que el vecino reciba y nadie llega al Recv ⇒ bloqueo mutuo.

Cada proceso tiene n/p+2n/p+2 elementos (2 celdas fantasma). Versión bloqueada: suaviza todo y luego Send/Recv de los bordes (el proceso espera). Versión no bloqueada (Algoritmos 2–3 de U3.3):

A(1) := smooth(0, 1, 2) // primero los bordes
A(n/p) := smooth(n/p-1, n/p, n/p+1)
Isend(A(1) → j-1), Isend(A(n/p) → j+1) // iniciar comunicación de bordes
Ireceive(A(n/p) ← j-1), Ireceive(A(1) ← j+1)
for i = 2 to n/p-1: A(i) := smooth(i-1, i, i+1) // el interior se calcula
MPI_Wait() // mientras viajan los mensajes

Solapa comunicación con cómputo: el costo de comunicación se esconde detrás del interior.

Con el código ejercicio03.cpp (5 procesos, todos los envíos MPI_Ssend): (3.1) ¿hay algún problema? (3.2) DAG y correctitud. (3.3) Corrija. (3.4) ¿Puede un DAG correctamente construido ocasionar un bloqueo?

Ver solución

Resolución propuesta (verifícala con los escenarios «PD03 Ej.3» y «corregido» del simulador).

  • 3.1 Deadlock: P2 primero espera Recv de P3 (tag 2), pero P3 solo envía después de recibir el resultado de P2. P0 y P1 quedan colgados en su Ssend a P2, y P4 esperando a P2. Además P2 recibe en a dos veces (pisa el valor).
  • 3.2 Aristas: P0→P2, P1→P2, P2→P3, P2→P4 y P3→P2: esta última forma un ciclo P2⇄P3 ⇒ el grafo de comunicación no es un DAG.
  • 3.3 Mover el Recv de P3 al final de P2 (y usar otra variable): P2 recibe de P0 y P1, calcula, envía a P3 y P4, y recién entonces recibe de P3. El grafo vuelve a ser acíclico en el tiempo.
  • 3.4 Un DAG bien construido no tiene ciclos de dependencia, así que la lógica no puede bloquear; pero la implementación sí: si el orden de las llamadas bloqueantes no respeta un orden topológico (p. ej. un proceso recibe antes de enviar lo que otro necesita, o Ssend/mensajes grandes cruzados), aparece un ciclo de espera aunque el DAG de datos sea acíclico. Este ejercicio es el ejemplo: el Recv de P3 estaba primero.

¿Es necesario usar comunicación no bloqueada? ¿Cómo se mide la latencia y el ancho de banda?

Ver solución

No: el patrón es alternado — cuando P0 envía, P1 ya está en su Recv, y viceversa; no hay espera circular (ni con Send ni con Ssend). Se itera 50 veces y el tiempo por mensaje es tfin−tini2⋅50\frac{t_{fin}-t_{ini}}{2\cdot50} (en µs con *1e6), excluyendo las primeras iteraciones (calentamiento). Con tamaños 8 B, 512 B, 32 kB y 2 MB: el tiempo de los mensajes chicos ≈ latencia α\alpha; la pendiente de tiempo vs tamaño ≈ β\beta (1/ancho de banda). Ssend tiene más latencia que Send para mensajes chicos (no puede usar el buffer).

Autoevaluación

0/5 correctas · 0 respondidas
1. Anillo: todos hacen MPI_Ssend al derecho y luego MPI_Recv del izquierdo.
2. Mismo anillo con MPI_Send y mensajes de 4 bytes:
3. MPI_Isend + MPI_Wait equivale a…
4. ¿Qué conviene llamar primero para evitar bloqueo mutuo?
5. Si el maestro hace Recv→Ssend y el resto Ssend→Recv en un anillo…

Fuente ·U3.3 Paralelismo Distribuido- No bloqueado.pdf · session3/ejemplo01a–d · practica_pc3/2025II-Ej01pingpong.c · practica_3_ofi/ejercicio03.cpp · CS4052-PD03-S02.pdf (Ej. 3) · U3.5 Ejercicios de repaso.pdf (Ej. 6, 8)