Sari la conținutul principal

Alte funcții de schimb de mesaje

MPI_Ssend și MPI_Issend

Funcția MPI_Ssend reprezintă o variantă sincronizată a funcției MPI_Send, care poate debloca comunicarea înainte ca procesul destinație să confirme că a primit cu succes toate datele trimise de către procesul sursă.

MPI_Ssend garantează că va fi deblocată comunicarea atunci când procesul destinație confirmă că a primit cu succes toate datele trimise de către procesul sursă, deci se poate considera că MPI_Ssend este o variantă mai sigură a funcției MPI_Send.

De notat faptul că MPI_Send se comportă ca MPI_Ssend atunci când se trimit date de dimensiuni mari.

Un dezavantaj al acestei funcții față de MPI_Send este că MPI_Ssend este mai predispusă la situații de deadlock.

Un exemplu de deadlock este următoarea situație:

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 este varianta non-blocantă a funcției MPI_Ssend.

MPI_Bsend și MPI_Ibsend

Funcția MPI_Bsend reprezintă o variantă a funcției MPI_Send, unde utilizatorul creează un buffer (folosind funcția MPI_Buffer_attach), prin intermediul căruia sunt schimbate mesajele. În acest buffer ajung doar mesajele care au fost trimise folosind MPI_Bsend.

Această funcție este utilă pentru situațiile când avem deadlock și când se trimit date de dimensiuni mari, similar ca la comunicarea non-blocantă. Diferența față de comunicarea non-blocantă este că la MPI_Bsend se garantează, atunci când funcția întoarce un rezultat, că datele au fost copiate integral în buffer, în timp ce MPI_Isend nu garantează acest lucru.

MPI_BSEND_OVERHEAD reprezintă un overhead de memorie, care este creat atunci când se apelează MPI_Bsend sau MPI_Ibsend.

MPI_Ibsend este varianta non-blocantă a funcției MPI_Bsend.

Exemplu de folosire:

#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");

// declarăm mărimea buffer-ului
int buffer_attached_size = MPI_BSEND_OVERHEAD + size * sizeof(int);
// se creează spațiul folosit pentru buffer
char* buffer_attached = malloc(buffer_attached_size);
// se creează buffer-ul MPI folosit pentru trimiterea mesajelor
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);

// se detașează buffer-ul folosit pentru trimiterea mesajelor și este distrus
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 și MPI_Irsend

MPI_Rsend reprezintă o variantă a funcției MPI_Send, care poate să fie folosită după execuția funcției de receive (doar atunci poate fi folosită această funcție, deși putem folosi atunci și MPI_Send).

Un exemplu de use case al acestei funcții este atunci când un proces A apelează MPI_Recv sau MPI_Irecv înainte de o barieră, iar un alt proces B apelează MPI_Rsend (unde B îi trimite date procesului A).

MPI_Irsend este varianta non-blocantă a funcției MPI_Rsend.

Exemplu de folosire:

#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

În cadrul funcției MPI_Sendrecv se execută combinat operațiile de send și de receive, care sunt blocante în acest caz, unde mai precis se trimite un mesaj către un proces și se primește un alt mesaj de la un proces (poate să fie același proces căruia i s-a trimis mesajul sau un proces diferit).

MPI_Sendrecv este util în situații în care poate apărea deadlock, de exemplu trimiterea înlănțuită sau în ciclu de mesaje, unde fiecare proces să facă mai întâi send, apoi receive, fapt ce duce la dependență ciclică de date, care rezultă în deadlock.

Exemplu de folosire:

#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) {
// procesul 0 trimite către procesul 1 și apoi așteaptă răspuns
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) {
// procesul 1 așteaptă mesaj de la procesul 0, apoi trimite răspuns
dest = 0;
source = 0;

outmsg = '1';
MPI_Sendrecv(&outmsg, 1, MPI_CHAR, dest, tag,
&inmsg, 1, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status);
}

// se folosește variabila de status pentru a afla detalii despre schimbul de date
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();
}