|
|
In the next webpage, we will study the (more difficult) NON-blocking send and receive.
Function Name | Usage |
---|---|
MPI_Send(void *buff,
int count, MPI_Datatype type, int dest, int tag, int comm) |
Send a point-to-point message
to process
dest
in the communication group
comm
The message is stored at memory location buff and consists of count items of datatype type The message will be tagged with the tag value tag The MPI_Send() function will only return if the message sent has been received by the destination. (It's safe to reuse the buffer buff right away). |
MPI_Recv(void *buff,
int count, MPI_Datatype type, int source, int tag, int comm, MPI_Status *status) |
Receive a point-to-point message
The message MUST BE from the process source in the communication group comm AND the message MUST BE tagged with the tag value tag The message received will be stored at memory location buff which will have space to store count items of datatype type When the function exits, the exit status is stored in status. Information about the received message is returned in a status variable, e.g.,:
In most cases, you know the structure of data received and then you can ignore the status value... If you pass NULL as status parameter, MPI will not return the status value. The MPI_Recv() function will only return if the desired message (from source with tag tag) has been received - or exits with an error code... |
|
/* This is a modified "Hello World" program */ #include "mpi.h" #include <iostream.h> #include <string.h> int main(int argc, char **argv) { char reply[100]; char buff[128]; int numprocs; int myid; int i; MPI_Status stat; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if (myid == 0) { /* ------------------------------------------------------------ This portion of code is executed by MPI 0 process - send ------------------------------------------------------------ */ printf("WE have %d processors\n", numprocs); for( i=1; i < numprocs; i++) { sprintf(buff, "Hello %d", i); MPI_Send(buff, 128, MPI_CHAR, i, 1234 /* tag !!!! */, MPI_COMM_WORLD); } for(i=1; i < numprocs; i++) { MPI_Recv(buff, 128, MPI_CHAR, i, 0, MPI_COMM_WORLD, &stat); cout << buff << endl; } } else { /* ------------------------------------------------------------ This portion of code is executed by the other MPI processes ------------------------------------------------------------ */ MPI_Recv(buff, 128, MPI_CHAR, 0, 0 /* tag !!!! */, MPI_COMM_WORLD, &stat); sprintf(reply, " |--> Hello 0, Processor %d is present and accounted for !", myid); strcat(buff, reply); MPI_Send(buff, 128, MPI_CHAR, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); } |
Demo instruction:
|
(This program demos that the source node parameter must also match for a messaeg to be received)
Demo instruction:
The "status" variable is used by MPI mesage receiving functions to obtain information about a received message.
Name of field | Usage |
---|---|
MPI_SOURCE | id of processor sending the message (integer) |
MPI_TAG | tag of the message (integer) |
MPI_ERROR | error code (integer) |
Without the type information, it is impossible to know the value conveyed in a representation. Example:
The answer depends on the type information:
Without the type information, you cannot answer correctly |
|
MPI_Send(buff, N, TYPE, dest, tag, comm); |
All this function does is:
(k is the size (number of bytes) of the type TYPE)
C/C++ Type | MPI symbolic constant |
---|---|
char | MPI_CHAR |
int | MPI_INT |
float | MPI_FLOAT |
double | MPI_DOUBLE |
MPI_Recv(buff, N, TYPE, dest, tag, comm, status); |
All this function does is:
(k is the size (number of bytes) of the type TYPE)
int main(int argc, char **argv) { char in[4]; // Send 4 characters int out; // Interprete as an integer int numprocs; int myid; int i; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if(myid == 0) { cout << "We have " << numprocs << " processors" << endl; MPI_Recv(&out, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, NULL); cout << "Received this number from proc 1: " << out << endl; } else if ( myid == 1 ) { in[0] = '2'; in[1] = 1; in[2] = 0; in[3] = 0; MPI_Send(in, 4, MPI_CHAR, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); } |
Program will print (256+50 = 306) - because ASCII code for '2' is 50...
(Note: lab1a is a Intel based machine and uses little endian storage, that's why we need to put the '2' in the first byte)
Demo instruction:
int main(int argc, char **argv) { char in[1]; // Send 1 character int out; // Interprete as an integer int numprocs; int myid; int i; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if(myid == 0) { cout << "We have " << numprocs << " processors" << endl; MPI_Recv(&out, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, NULL); cout << "Received this number from proc 1: " << out << endl; } else if ( myid == 1 ) { in[0] = '2'; // ONLY 1 Character MPI_Send(in, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); } |
Program prints a random number - because the last 3 characters are "random" - the first ASCII code is 50, re[resenting the character '2' - see the output of the program:
c[0] = 2 <- this was initialized. c[1] = H <- the next 3 characters ar "random"... c[2] = 3 c[3] = |
Demo instruction:
|
Pseudo code:
/* ============================================================ Prepare MPI ============================================================ */ MPI_Init(&argc,&argv); // Initialize MPI_Comm_size(MPI_COMM_WORLD, &num_procs); // Get # processors MPI_Comm_rank(MPI_COMM_WORLD, &myid); N = # intervals used to do the integration... w = 1.0/(double) N; mypi = 0.0; // My partial sum (from a MPI processor) Compute my part of the partial sum based on 1. myid 2. num_procs if ( I am the master of the group ) { for ( i = 1; i < num_procs; i++) { receive the partial sum from MPI processor i; Add partial sum to my own partial sum; } Print final total; } else { Send my partial sum to the master of the MPI group; } MPI_Finalize(); |
double f(double a) { return( 2.0 / sqrt(1 - a*a) ); } int main(int argc, char *argv[]) { int N; // Number of intervals double w, x; // width and x point int i, myid; double mypi, others_pi; MPI_Init(&argc,&argv); // Initialize MPI_Comm_size(MPI_COMM_WORLD, &num_procs); // Get # processors MPI_Comm_rank(MPI_COMM_WORLD, &myid); N = atoi(argv[1]); w = 1.0/(double) N; mypi = 0.0; /* --------------------------------------------------------- Every MPI process computes a partial sum for the integral --------------------------------------------------------- */ for (i = myid; i < N; i = i + num_procs) { x = w*(i + 0.5); mypi = mypi + w*f(x); } /* ----------------------------- Now put the sum together... ----------------------------- */ if ( myid == 0 ) { /* ---------------------------------------------------- Proc 0 collects and others send data to proc 0 ---------------------------------------------------- */ for (i = 1; i < num_procs; i++) { MPI_Recv(&others_pi, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, NULL); mypi += others_pi; } cout << "Pi = " << mypi<< endl << endl; // Output... } else { /* --------------------------------------------- The other processors send their partial sum to processor 0 --------------------------------------------- */ MPI_Send(&mypi, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); } |
Memory is NOT shared
by different MPI Processes.
In other words:
|
Demo instruction: