Skip to main content

Non-blocking Functions

So far in the MPI laboratories, you have worked with blocking communication functions (MPI_Send, MPI_Recv). In blocking communication, send and receive functions block until the buffer used for message transmission can be reused, specifically:

  • For send (MPI_Send), a result is returned when the buffer where the data is placed for transmission can be reused (until then, sending is blocked).
  • For receive (MPI_Recv), a result is returned when all data transmitted through the buffer can be processed (until then, receiving is blocked).

Message communication is divided into two main categories:

  • Blocking, which is synchronous
  • Non-blocking, which is asynchronous

Blocking sending comes in four modes:

  • Standard (MPI_Send)
  • Synchronized (MPI_Ssend)
  • Buffered (MPI_Bsend)
  • Ready (MPI_Rsend)

Regarding non-blocking communication, the data sending (MPI_Isend) and receiving (MPI_Irecv) functions immediately return a result. Thus, there are no guarantees regarding the completion of data transmission. To ensure that the data has been sent and received correctly and completely, we can use the MPI_Test and MPI_Wait functions, which we will discuss in detail below.

Non-blocking communication is useful for situations where deadlock may occur or when sending large amounts of data.

MPI_Isend

Function signature:

int MPI_Isend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request)

The parameters of the MPI_Isend function behave the same as the parameters of the MPI_Send function, but what is added is the *MPI_Request request parameter, where MPI_Request is a structure used for testing the sending and receiving of data.

MPI_Irecv

Function signature:

int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request)

Unlike MPI_Recv, MPI_Irecv does not have an MPI_Status parameter, which is replaced by the MPI_Request parameter. Everything else remains identical to MPI_Recv.

MPI_Test

Function signature:

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)

Where:

  • request is the one from the send or receive function whose result is being tested.
  • flag indicates whether the send or receive operation has been successfully completed.
  • status contains message-related data (relevant for the receive operation).

MPI_Wait

Function signature:

int MPI_Wait(MPI_Request *request, MPI_Status *status)

Where:

  • request is the one from the send or receive function whose result is being tested.
  • status contains message-related data (relevant for the receive operation).

First, it is tested using MPI_Test whether a function (send or receive) has finished sending/receiving the data. If the send/receive operation has not been completed, then MPI_Wait will be used.

img

Example of send-receive using non-blocking functions:

#include <mpi.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define MAX_LEN 10

int main(int argc, char *argv[]) {
int numtasks, rank, dest, source, count, flag, tag = 1;
char inmsg[MAX_LEN], outmsg[] = "Hello";
MPI_Status status;
MPI_Request request;

MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

if (rank == 0) {
// process 0 sends to process 1
dest = 1;
source = 1;

sleep(3);

MPI_Isend(outmsg, strlen(outmsg) + 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD, &request);
MPI_Test(&request, &flag, &status);

if (flag) {
printf("[P0] The send operation is over\n");
} else {
printf("[P0] The send operation is not over yet\n");
MPI_Wait(&request, &status);
}

printf("[P0] The send operation is definitely over\n");
} else if (rank == 1) {
// process 1 is waiting for message from process 0
dest = 0;
source = 0;

sleep(1);

MPI_Irecv(inmsg, MAX_LEN, MPI_CHAR, source, tag, MPI_COMM_WORLD, &request);
MPI_Test(&request, &flag, &status);

if (flag) {
printf("[P1] The receive operation is over\n");
} else {
printf("[P1] The receive operation is not over yet\n");
MPI_Wait(&request, &status);
}

/*
the status variable is used to find out details about the data exchange
MPI_Get_count - calculates how many elements have been received
*/
MPI_Get_count(&status, MPI_CHAR, &count);
printf("[P1] Received %d char(s) from process %d with tag %d: %s\n", count, status.MPI_SOURCE, status.MPI_TAG, inmsg);
}

MPI_Finalize();
}