Sari la conținutul principal

Funcții nonblocante

Până acum în cadrul laboratoarelor de MPI ați lucrat cu funcții de comunicare blocante (MPI_Send, MPI_Recv). În cadrul comunicării blocante, funcțiile de send și de receive se blochează până când buffer-ul folosit pentru transmisia de mesaje poate fi refolosit, mai precis în felul următor:

  • la send (MPI_Send) se întoarce un rezultat când buffer-ul unde se pun datele pentru transmisie poate folosit din nou (până atunci trimiterea este blocată).
  • la receive (MPI_Recv) se întoarce un rezultat când toate datele transmise prin buffer pot fi prelucrate (până atunci primirea este blocată).

Comunicarea mesajelor este împărțită în două mari categorii:

  • blocantă, care este sincronă
  • non-blocantă, care este asincronă

Trimiterea blocantă este în patru moduri:

  • standard (MPI_Send)
  • sincronizată (MPI_Ssend)
  • buffered (MPI_Bsend)
  • ready (MPI_Rsend)

În ceea ce privește comunicarea non-blocantă, funcțiile de trimitere (MPI_Isend) și de primire (MPI_Irecv) a datelor întorc imediat un rezultat. Astfel, nu avem rezultate sigure legate de terminarea trimiterii de date, așadar ca să ne asigurăm că datele au fost trimise și primite corect și complet putem folosi funcțiile MPI_Test și MPI_Wait, despre care vom vorbi în detaliu mai jos.

Comunicarea non-blocantă este utilă pentru situații în care avem deadlock sau în care se trimit date de dimensiuni mari.

MPI_Isend

Semnătura funcției:

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

Parametrii funcției MPI_Isend se comportă la fel ca parametrii funcției MPI_Send, însă ce este în plus este parametrul *MPI_Request request, MPI_Request fiind o structură folosită pentru testarea trimiterii și primirii datelor.

MPI_Irecv

Semnătura funcției:

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

Spre deosebire de MPI_Recv, MPI_Irecv nu are parametru de MPI_Status, care este înlocuit de parametrul MPI_Request. În rest totul este identic ca la MPI_Recv.

MPI_Test

Semnătura funcției:

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

Unde:

  • request este cel de la funcția de send sau de receive, al cărui rezultat este testat
  • flag arată dacă operația de send sau de receive s-a terminat cu succes
  • status conține date legate de mesaj (relevante la operația de receive)

MPI_Wait

Semnătura funcției:

int MPI_Wait(MPI_Request *request, MPI_Status *status)

Unde:

  • request este cel de la funcția de send sau de receive, al cărui rezultat este testat
  • status conține date legate de mesaj (relevante la operația de receive)

Mai întâi se testează folosind MPI_Test dacă o funcție (send sau receive) a terminat de trimis / primit datele. Dacă nu s-a completat operația de send / receive, atunci se va folosi MPI_Wait.

img

Exemplu de send-receive folosind funcții non-blocante:

#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) {
// procesul 0 trimite catre procesul 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) {
// procesul 1 asteapta mesaj de la procesul 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);
}

/*
se foloseste variabila de status pentru a afla detalii despre schimbul de date
MPI_Get_count - calculeaza cate elemente s-au primit
*/
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();
}