MPI Non-Blocking Communication is a message passing paradigm where send and receive operations return immediately without waiting for the message transfer to complete, allowing the program to perform computation while data is being transmitted in the background — this overlap of communication and computation is the primary technique for hiding network latency in distributed parallel applications.
Non-Blocking Operation Basics:
- MPI_Isend: initiates a send operation and returns immediately with a request handle — the send buffer must not be modified until the operation completes, as the MPI library may still be reading from it
- MPI_Irecv: posts a receive buffer and returns immediately — the receive buffer contents are undefined until the operation is confirmed complete via MPI_Wait or MPI_Test
- MPI_Request: an opaque handle returned by non-blocking operations — used to query status (MPI_Test) or block until completion (MPI_Wait)
- Completion Semantics: for MPI_Isend, completion means the send buffer can be reused (not that the message was received) — for MPI_Irecv, completion means the message has been fully received into the buffer
Completion Functions:
- MPI_Wait: blocks until the specified non-blocking operation completes — equivalent to polling MPI_Test in a loop but may yield the processor to the MPI progress engine
- MPI_Test: non-blocking check of whether an operation has completed — returns a flag indicating completion status, allowing the program to do useful work between checks
- MPI_Waitall/MPI_Testall: wait for or test completion of an array of requests — essential when managing multiple outstanding non-blocking operations simultaneously
- MPI_Waitany/MPI_Testany: completes when any one of the specified operations finishes — useful for processing results as they arrive rather than waiting for all to complete
Overlap Patterns:
- Halo Exchange: in stencil computations, post MPI_Irecv for ghost cells, then post MPI_Isend for boundary cells, compute interior cells while communication proceeds, call MPI_Waitall before computing boundary cells — hides 80-95% of communication latency for sufficiently large domains
- Pipeline Overlap: divide data into chunks, send chunk k while computing on chunk k-1 — software pipelining that converts latency-bound communication into bandwidth-bound
- Double Buffering: alternate between two message buffers — while one buffer is being communicated the other is being computed on — ensures continuous progress of both computation and communication
- Non-Blocking Collectives (MPI 3.0): MPI_Iallreduce, MPI_Ibcast, MPI_Igather allow overlapping collective operations with computation — critical for gradient aggregation in distributed deep learning
Progress Engine Considerations:
- Asynchronous Progress: actual overlap depends on the MPI implementation's progress engine — some implementations require the application to periodically enter the MPI library (via MPI_Test) to make progress on background operations
- Hardware Offload: InfiniBand and similar RDMA-capable networks can progress operations entirely in hardware without CPU involvement — true asynchronous overlap regardless of application behavior
- Thread-Based Progress: some MPI implementations spawn background threads to drive communication — requires MPI_Init_thread with MPI_THREAD_MULTIPLE support
- Manual Progress: calling MPI_Test periodically in compute loops ensures progress — typically every 100-1000 iterations provides sufficient progress without significant overhead
Persistent Communication:
- MPI_Send_init/MPI_Recv_init: creates a persistent request that can be started multiple times with MPI_Start — amortizes setup overhead when the same communication pattern repeats across iterations
- MPI_Start/MPI_Startall: activates persistent requests — equivalent to calling MPI_Isend/MPI_Irecv but with pre-computed internal state
- Performance Benefit: persistent operations reduce per-message overhead by 20-40% for repeated communication patterns — the MPI library can precompute routing, buffer management, and protocol selection
- Partitioned Communication (MPI 4.0): extends persistent operations to allow partial buffer completion — a send buffer can be filled incrementally with MPI_Pready marking completed portions
Best Practices:
- Post Receives Early: always post MPI_Irecv before the matching MPI_Isend to avoid unexpected message buffering — eager protocol messages that arrive before a posted receive require system buffer copies
- Minimize Request Lifetime: complete non-blocking operations as soon as the overlap opportunity ends — long-lived requests consume MPI internal resources and may limit the number of outstanding operations
- Avoid Deadlocks: non-blocking operations don't deadlock by themselves, but improper wait ordering can — always use MPI_Waitall for groups of related operations rather than sequential MPI_Wait calls that might create circular dependencies
Non-blocking communication transforms network latency from a serial bottleneck into a parallel resource — well-optimized MPI applications achieve 85-95% computation-communication overlap, approaching the theoretical peak throughput of the underlying network.