Table of Contents

Send modes (MPI)

MPI_Send has a behaviour that surprises many first-time users: whether it blocks or returns immediately depends on message size and the implementation's internal buffer. For small messages, most implementations copy the data into an internal buffer and return right away. For messages larger than that buffer, MPI_Send blocks until the receiver posts a matching MPI_Recv. Code that runs correctly with small test data can deadlock in production when message sizes grow, because the buffering that masked the cyclic dependency disappears. The four send modes exist to make this behaviour explicit and portable.

MPI_Ssend (synchronous send) never buffers. It blocks until the receiver has posted a matching MPI_Recv, which makes it the most useful tool for debugging deadlocks: replacing MPI_Send with MPI_Ssend removes buffering as a variable, so if the code hangs it definitely has a cyclic dependency rather than just exceeding buffer limits. MPI_Bsend (buffered send) always returns immediately by copying into a user-supplied buffer attached with MPI_Buffer_attach. MPI_Rsend (ready send) asserts that a matching receive is already posted; behaviour is undefined if it is not, but it can skip handshake overhead on implementations that exploit the guarantee.

// synchronous: blocks until receiver has called MPI_Recv
MPI_Ssend(&x, 1, MPI_INT, dest, 0, MPI_COMM_WORLD);
 
// buffered: always returns immediately; user manages the buffer
char buf[MPI_BSEND_OVERHEAD + sizeof(int)];
MPI_Buffer_attach(buf, sizeof(buf));
MPI_Bsend(&x, 1, MPI_INT, dest, 0, MPI_COMM_WORLD);
MPI_Buffer_detach(&buf_ptr, &buf_size);  // blocks until buffer is drained

All four modes have non-blocking variants: MPI_Issend, MPI_Ibsend, MPI_Irsend. They follow the same request/wait pattern as MPI_Isend.