Receive
MPI_Recv
MPI_Recv reprezintă funcția prin care un proces primește date de la un alt proces. Semnătura funcției este următoarea:
int MPI_Recv(void* data, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm communicator, MPI_Status* status)
Unde:
- data (↑) - reprezintă datele primite de la procesul sursă de către procesul destinație
- count (↓) - dimensiunea datelor primite
- datatype (↓) - tipul datelor primite
- source (↓) - rangul / identificatorului procesului sursă, care trimite datele
- tag (↓) - identificator al mesajului
- communicator (↓) - comunicatorul în cadrul căruia se face trimiterea datelor între cele două procese
- status - conține date despre mesajul primit, MPI_Status fiind o structură ce conține informații despre mesajul primit (sursa, tag-ul mesajului, dimensiunea mesajului). Dacă nu dorim să ne folosim de datele despre mesajul primit, punem MPI_STATUS_IGNORE, prin care se ignoră status-ul mesajului.
În situația în care procesul P apelează funcția de MPI_Recv(), el se va bloca până va primi toate datele asteptate, astfel că dacă nu va primi nimic sau ceea ce primește este insuficient, P va rămâne blocat. Adică MPI_Recv() se termină doar în momentul în care buffer-ul a fost umplut cu datele așteptate.
Structura MPI_Status include următoarele câmpuri:
- int count - dimensiunea datelor primite
- int MPI_SOURCE - identificatorul procesului sursă, care a trimis datele
- int MPI_TAG - tag-ul mesajului primit
MPI_Recv este o funcție blocantă, mai precis programul se poate bloca până când se execută acțiunea de trimitere a mesajului către procesul sursă.
Un exemplu de program în care un proces trimite un mesaj către un alt proces:
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int numtasks, rank, len;
char hostname[MPI_MAX_PROCESSOR_NAME];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks); // Total number of processes.
MPI_Comm_rank(MPI_COMM_WORLD,&rank); // The current process ID / Rank.
MPI_Get_processor_name(hostname, &len);
srand(42);
int random_num = rand();
printf("Before send: process with rank %d has the number %d.\n", rank,
random_num);
if (rank == 0) {
MPI_Send(&random_num, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
} else {
MPI_Status status;
MPI_Recv(&random_num, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Process with rank %d, received %d with tag %d.\n",
rank, random_num, status.MPI_TAG);
}
printf("After send: process with rank %d has the number %d.\n", rank,
random_num);
MPI_Finalize();
}
Când un proces X trimite un mesaj către un proces Y, tag-ul T al mesajului din MPI_Send, executat de procesul X, trebuie să fie același cu tag-ul mesajului din MPI_Recv, executat de procesul Y, deoarece procesul Y așteaptă un mesaj care are tag-ul T, altfel, dacă sunt tag-uri diferite, programul se va bloca.
O ilustrație a modului cum funcționează împreună funcțiile MPI_Send și MPI_Recv:
Mai jos aveți un exemplu în care un proces trimite un întreg array de 100 de elemente către un alt proces:
Exemplu
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int numtasks, rank, len;
int size = 100;
char hostname[MPI_MAX_PROCESSOR_NAME];
int arr[size];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Get_processor_name(hostname, &len);
srand(42);
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");
MPI_Send(arr, size, MPI_INT, 1, 1, MPI_COMM_WORLD);
printf("Process with rank [%d] sent the array.\n", rank);
} 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();
}