More than two processes
Overview
Teaching: 20 min
Exercises: 15 minQuestions
How do we manage communication among many processes?
Objectives
Write code to pass messages along a chain
Write code to pass messages around a ring
MPI programs routinely work with thousands of processors. In our first message we used if
s to write an MPI_Ssend
and an MPI_Recv
for each process. Applying this idea niavely to large numbers of processes would lead to something like:
if(rank==0){...}
if(rank==1){...}
if(rank==2){...}
...
which gets a little tedious somewhere at or before rank=397. More typically programs calculate the ranks they need to communicate with. Let’s imagine processors in a line. How could we calculate the left and right neighbour ranks?
One way to calculate our neighbour’s ranks cloud be left=rank-1
, and right=rank+1
. Let’s think for a moment what would happen if we wanted to send a message to the right. Each process would have an MPI_Ssend
call to send to their right neighbour and each process would also have an MPI_Recv
call to receive a message from their left neighbour. But what will happen at the ends? It turns out that there are special constants that can be used as sources and destinations of messages. If MPI_PROC_NULL
is used the send or receive is skipped.
MPI_ANY_SOURCE
MPI_ANY_SOURCE
is similar toMPI_PROC_NULL
but can only be used when receiving a message. It will allow a receive call to accept a message sent from any process.
Let’s try out sending a set of messages to the processes on the right.
$ cp firstmessage.c secondmessage.c
Remember to replace .c
with .f90
if working with Fortran code.
#include <stdio.h>
#include <mpi.h>
int main(int argc, char **argv) {
int rank, size, ierr;
int left, right;
int tag=1;
double msgsent, msgrcvd;
MPI_Status rstatus;
ierr=MPI_Init(&argc, &argv);
ierr=MPI_Comm_size(MPI_COMM_WORLD, &size);
ierr=MPI_Comm_rank(MPI_COMM_WORLD, &rank);
left=rank-1;
if(left<0){
left=MPI_PROC_NULL;
}
right=rank+1;
if(right>=size){
right=MPI_PROC_NULL;
}
msgsent=rank*rank;
msgrcvd=-999.;
int count=1;
ierr=MPI_Ssend(&msgsent, count, MPI_DOUBLE, right,tag,
MPI_COMM_WORLD);
ierr=MPI_Recv(&msgrcvd, count, MPI_DOUBLE, left, tag, MPI_COMM_WORLD,
&rstatus);
printf("%d: Sent %lf and got %lf\n", rank, msgsent, msgrcvd);
ierr=MPI_Finalize();
return 0;
}
NOTE MPI_DOUBLE
is used in C to map to the C double
datatype, while MPI_DOUBLE_PRECISION
is used in Fortran to map to Fortran double precision
datatype.
program secondmessage
use mpi
implicit none
integer :: ierr, rank, comsize
integer :: left, right
integer :: tag
integer :: status(MPI_STATUS_SIZE)
double precision :: msgsent, msgrcvd
call MPI_INIT(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD,rank,ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD,comsize,ierr)
left = rank-1
if (left < 0) left = MPI_PROC_NULL
right = rank+1
if (right >= comsize) right = MPI_PROC_NULL
msgsent = rank*rank
msgrcvd = -999.
tag = 1
call MPI_Ssend(msgsent, 1, MPI_DOUBLE_PRECISION, right, &
tag, MPI_COMM_WORLD, ierr)
call MPI_Recv(msgrcvd, 1, MPI_DOUBLE_PRECISION, left, &
tag, MPI_COMM_WORLD, status, ierr)
print *, rank, 'Sent ', msgsent, 'and recvd ', msgrcvd
call MPI_FINALIZE(ierr)
end program secondmessage
Now let’s compile our program with either
$ mpicc -o secondmessage secondmessage.c
or
$ mpif90 -o secondmessage secondmessage.f90
and run with
$ mpirun -np 4 ./secondmessage
0: Sent 0.000000 and got -999.000000
1: Sent 1.000000 and got 0.000000
2: Sent 4.000000 and got 1.000000
3: Sent 9.000000 and got 4.000000
Periodic boundaries
In many problems it is helpful to wrap data around at the boundaries of the grid being modelled. For example later in our 1D diffusion exercise, we will use periodic boundary conditions which allows heat flow out of one end of our thin wire to enter the other end making our thin wire a ring. This sort of periodic boundary condition often works well in physical simulations.
Let’s try having our messages wrap around from our right most process to our left most process.
Add periodic message passing
$ cp secondmessage.c thirdmessage.c $ nano thirdmessage.c
Remember to replace .c with .f90 if working with Fortran code. And modify the code so that
left
is eithersize-1
for C orcomsize-1
for Fortran when at the inner most edge and so thatright
is 0 at the outer most edge.Then compile and run.
Hint: you may need to press
ctrl+c
after running the program.Solution
. . . left=rank-1; if(left<0){ left=size-1; } right=rank+1; if(right>=size){ right=0; } . . .
. . . left = rank-1 if (left < 0) left = comsize-1 right = rank+1 if (right >= comsize) right = 0 . . .
Then compile with either
$ mpicc -o thirdmessage thirdmessage.c
or
$ mpif90 -o thirdmessage thirdmessage.f90
and run with
$ mpirun -np 4 ./thirdmessage
The program hangs and must be killed by pressing
ctrl+c
. This is because theMPI_Ssend
call blocks until a matchingMPI_Recv
has been initiated.
Key Points
A typical MPI process calculates which other processes it will communicate with.
If there is a closed loop of procs sending to one another, there is a risk of deadlock.
All sends and receives must be paired, at time of sending.
Types MPI_DOUBLE (C), MPI_DOUBLE_PRECISION (Ftn)
Constants MPI_PROC_NULL, MPI_ANY_SOURCE