Other messaging functions
MPI_Ssend and MPI_Issend
The MPI_Ssend function is a synchronized variant of the MPI_Send function, which can release the communication before the destination process confirms that it has successfully received all the data sent by the source process.
MPI_Ssend guarantees that communication will be released when the destination process confirms that it has successfully received all data sent by the source process, so MPI_Ssend can be considered a safer variant of *MPI_Send *.
Note that MPI_Send behaves like MPI_Ssend when sending large data.
A disadvantage of this function over MPI_Send is that MPI_Ssend is more prone to deadlock situations.
An example of a deadlock is the following situation:
if (rank == 0) {
MPI_Send(&num1, SIZE, MPI_INT, 1, 0, MPI_COMM_WORLD);
// MPI_Ssend(&num1, SIZE, MPI_INT, 1, 0, MPI_COMM_WORLD);
MPI_Recv(&num2, SIZE, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
} else {
MPI_Send(&num2, SIZE, MPI_INT, 0, 0, MPI_COMM_WORLD);
// MPI_Ssend(&num2, SIZE, MPI_INT, 1, 0, MPI_COMM_WORLD);
MPI_Recv(&num1, SIZE, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
MPI_Issend is the non-blocking variant of the MPI_Ssend function.
MPI_Bsend and MPI_Ibsend
The MPI_Bsend function is a variant of the MPI_Send function, where the user creates a buffer (using the MPI_Buffer_attach function), through which messages are exchanged. Only messages that were sent using MPI_Bsend end up in this buffer.
This function is useful for situations when we have deadlock and when sending large data, similar to non-blocking communication. The difference with non-blocking communication is that MPI_Bsend guarantees, when the function returns a result, that the data has been fully copied into the buffer, while MPI_Isend does not.
MPI_BSEND_OVERHEAD represents a memory overhead, which is created when calling MPI_Bsend or MPI_Ibsend.
MPI_Ibsend is the non-blocking variant of the MPI_Bsend function.
Example of use:
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int numtasks, rank;
int size = 100;
int arr[size];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
for (int i = 0; i < size; i++) {
arr[i] = i;
}
printf("Process with rank [%d] has the following array:\n", rank);
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// declare the buffer size
int buffer_attached_size = MPI_BSEND_OVERHEAD + size * sizeof(int);
// create the space used for the buffer
char* buffer_attached = malloc(buffer_attached_size);
// create the MPI buffer used for sending messages
MPI_Buffer_attach(buffer_attached, buffer_attached_size);
MPI_Bsend(&arr, size, MPI_INT, 1, 1, MPI_COMM_WORLD);
printf("Process with rank [%d] sent the array.\n", rank);
// the buffer used for sending messages is detached and destroyed
MPI_Buffer_detach(&buffer_attached, &buffer_attached_size);
free(buffer_attached);
} else {
MPI_Status status;
MPI_Recv(&arr, size, MPI_INT, 0, 1, MPI_COMM_WORLD, &status);
printf("Process with rank [%d], received array with tag %d.\n",
rank, status.MPI_TAG);
printf("Process with rank [%d] has the following array:\n", rank);
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
MPI_Finalize();
}
MPI_Rsend and MPI_Irsend
MPI_Rsend represents a variant of the MPI_Send function, which can be used after the execution of the receive function (only then can this function be used, although we can also use MPI_Send then).
An example use case of this function is when a process A calls MPI_Recv or MPI_Irecv before a barrier and another process B calls MPI_Rsend (where B sends data to process A ).
MPI_Irsend is the non-blocking variant of the MPI_Rsend function.
Example of use:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char* argv[]) {
int size, rank, value;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
MPI_Barrier(MPI_COMM_WORLD);
value = 12345;
printf("[P0] MPI process sends value %d.\n", value);
MPI_Rsend(&value, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
} else {
MPI_Request request;
MPI_Status status;
int flag;
MPI_Irecv(&value, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &request);
MPI_Barrier(MPI_COMM_WORLD);
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);
}
printf("[P1] MPI process received value %d.\n", value);
}
MPI_Finalize();
}
MPI_Sendrecv
Within the MPI_Sendrecv function, the send and receive operations are combined, which are blocking in this case, where more precisely a message is sent to a process and another message is received from a process (it can be the same process to which the message was sent or a different process).
MPI_Sendrecv is useful in situations where deadlock can occur, for example chained or message-cycle sending, where each process first sends, then receives, which leads to cyclic data dependency, resulting in deadlock .
Example of use:
#include <mpi.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
int numtasks, rank, dest, source, count, tag = 1;
char inmsg, outmsg;
MPI_Status status;
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 and then waits for response
dest = 1;
source = 1;
outmsg = '0';
MPI_Sendrecv(&outmsg, 1, MPI_CHAR, dest, tag,
&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status);
} else if (rank == 1) {
// process 1 waits for message from process 0, then sends response
dest = 0;
source = 0;
outmsg = '1';
MPI_Sendrecv(&outmsg, 1, MPI_CHAR, dest, tag,
&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status);
}
// use the status variable to find out details about the data exchange
MPI_Get_count(&status, MPI_CHAR, &count);
printf("Process %d received %d char(s) from process %d with tag %d: %c\n",
rank, count, status.MPI_SOURCE, status.MPI_TAG, inmsg);
MPI_Finalize();
}