How does the MPI_SENDRECV work? - mpi

I have a question about MPI_SENDRECV.
here is an example:
PROGRAM sendrecv
IMPLICIT NONE
INCLUDE "mpif.h"
INTEGER a,b,myrank,nprocs,ierr
integer istat(MPI_STATUS_SIZE)
CALL MPI_INIT(ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD, nprocs, ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
if (myrank.eq.0) then
a=1;b=3
else
a=2;b=4
endif
if (myrank == 0) then
call MPI_SENDRECV(b,1,MPI_REAL,1,0,
. a,1,MPI_REAL,1,0,
. MPI_COMM_WORLD,istat,ierr)
elseif (myrank == 1) then
call MPI_SENDRECV(b,1,MPI_REAL,0,0,
. a,1,MPI_REAL,0,0,
. MPI_COMM_WORLD,istat,ierr)
end if
if (myrank.eq.0) then
write(*,*) b,a
else
write(*,*) a,b
endif
CALL MPI_FINALIZE(ierr)
END
After this we get 3 4 and 3 4.
My question is that is we replace the MPI_SENDRECV(if we assume that MPI_SENDRECV is send first and then receive)
if (myrank == 0) then
call MPI_SEND(b,1,MPI_REAL,1,0,MPI_COMM_WORLD,ierr)
call MPI_RECV(a,1,MPI_REAL,0,0,MPI_COMM_WORLD,istat,ierr)
elseif (myrank == 1) then
call MPI_SEND(b,1,MPI_REAL,0,0,MPI_COMM_WORLD,ierr)
call MPI_RECV(a,1,MPI_REAL,1,0,MPI_COMM_WORLD,istat,ierr)
end if
Then this will be deadlock
So this means that MPI_SENDRECV it not first sends and then receives, but sends ans receives simultaneously,right?

You are right, MPI_Sendrecv is not the same a send followed by a receive. Think of it as an MPI_Isend, MPI_Irecv, and a pair of MPI_Waits. So in effect, the send and receive proceed in parallel.
Incidentally, this is how it is commonly implemented in the MPI libraries.
If you wanted to fix the deadlock in your second example, the processes would have to issue the sends and receives in a different order. So rank 0 would issue a send followed by receive, and rank 1 - a receive followed by a send.

Even though the message is routed to receive process B , process B still has to acknowledge that it wants to receive A's data. Once it does this, the data has been transmitted. Process A is acknowledged that the data has been transmitted and may go back to work.
So your second code can't satisfy the condition, which seems like that you don't answer the call by others. It should be like the follows:
if (myrank == 0) then
call MPI_SEND(b,1,MPI_REAL,1,0,MPI_COMM_WORLD,ierr)
call MPI_RECV(a,1,MPI_REAL,1,0,MPI_COMM_WORLD,istat,ierr)
elseif (myrank == 1) then
call MPI_SEND(b,1,MPI_REAL,0,0,MPI_COMM_WORLD,ierr)
call MPI_RECV(a,1,MPI_REAL,0,0,MPI_COMM_WORLD,istat,ierr)
end if

Related

How do I check that all MPI procs were used to call a procedure?

I have designed a procedure that must be called by all processors in the communicator in order to function properly. If the user called it with only the root rank, I want the procedure to know this and then produce a meaningful error message to the user of the procedure. At first I thought of having the procedure call a checking routine shown below:
subroutine AllProcsPresent
! Checks that all procs have been used to call this procedure
use MPI_stub, only: nproc, Allreduce
integer :: counter
counter=1
call Allreduce(counter) ! This is a stub procedure that will add "counter" across all procs
if (counter(1)==get_nproc()) then
return
else
print *, "meaningful error"
end if
end subroutine AllProcsPresent
But this won't work because the Allreduce is going to wait for all procs to check in and if only root was used to do the call, the other procs will never arrive. Is there a way to do what I'm trying to do?
There's not much you can do here. You might want to look at 'collecheck' for ideas, but it's hard to find a good resource for that package. Here's its git home:
http://git.mpich.org/mpe.git/tree/HEAD:/src/collchk
If you look at 'NOTES' there's an item about "call consistency" described as "Ensures that all processes in the communicator have made the same call in a given event". Hope that can give you some ideas.
Ensuring that a collective operation is entered by all ranks within a communicator is the responsibility of the programmer.
However, you might consider using the MPI 3.0 non-blocking collective MPI_Ibarrier with an MPI_Test loop and time out. However, non-blocking collectives can't be cancelled, so if the other ranks do not join in the operation within your time out, you will have to abort the entire job. Something like:
void AllPresent(MPI_Comm comm, double timeout) {
int all_here = 0;
MPI_Request req;
MPI_Ibarrier(comm, &req);
double start_time = MPI_Wtime();
do {
MPI_Test(&req, &all_here, MPI_STATUS_IGNORE);
sleep(0.01);
double now = MPI_Wtime();
if (now - start_time > timeout) {
/* Print an error message */
MPI_Abort(comm, 1);
}
} while (!all_here);
/* Run your procedure now */
}

Golang : some questions on channel

http://play.golang.org/p/uRHG-Th_2P
I am having hard time understanding the concept of channel
package main
import (
"fmt"
)
func Fibonacci(limit int, chnvar chan int) {
x, y := 0, 1
for i := 0; i < limit; i++ {
chnvar <- x
x, y = y, x+y
}
close(chnvar)
v, ok := <-chnvar
fmt.Println(v, ok)
}
func main() {
chn := make(chan int, 10)
go Fibonacci(cap(chn), chn)
for elem := range chn {
fmt.Printf("%v ", elem)
}
}
//1 1 2 3 5 8 13 21 34
1)
How do I get false value from the line
v, ok := <-chnvar
It says false if there are no more values to get.
and also false if the channel is closed.
But in this case, the channel is closed but(?) still get the true value.
And if I take out the close, it panics.
How and why it returns true here?
2)
The line
go Fibonacci(cap(chn), chn)
also runs without goroutine.
What is the difference? Just matter of performance.
Thanks in advance
Your Fibonacci function stuffs 10 values into the channel (which has a buffer of 10 values), and then closes it. Assuming the v, ok <- chnvar statement executes before the main goroutine reads everything out of the channel (very likely, but not guaranteed), there will be a value to read so ok will be true.
If you remove the close call, the for loop in the main goroutine will eventually empty the channel's buffer and block waiting for more data. Since there is no other goroutine active to write to the channel, the runtime detects this as a deadlock.
Your sample program runs with Fibonacci called directly (not as a goroutine) because the channel it writes to is buffered, and it never overruns the buffer. Therefore it can complete without blocking and allows execution to continue to the rest of the main function.
If the channel was not buffered, or you wrote more values than would fit in the buffer, then Fibonacci would block waiting for some other goroutine to read something from the channel.
1)
The Go specification states for channel receive operations (my emphasis):
x, ok := <-ch
The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.
That is, because the buffered channel is not empty and you have successfully received a value (0), ok will be true. You won't receive false until the channel has been emptied.
2)
By running the Fibonacci(cap(chn), chn) in it's own Go routine, main can start receiving and processing (printing out) out values while the Fibonacci function is still feeding new values to the channel.
In your case, this probably never happens since the function will have filled up the buffer and completed before main gets a chance to process anything.
If it would not be running in a Go routine, Fibonacci would first need to produce all the values before they can be processed further by main.

MPI_Sendrecv deadlock

Could anyone help me to fix the bug in the following simple MPI prog. I was trying to use MPI_Sendrecv to send the "c" value from rank 1 to 2, and they print it from rank 2.
But, the following code ends with a deadlock.
What is the mistake, how to correctly use MPI_Sendrecv (in this situation)
#include<stdio.h>
#include"mpi.h"
int main (int argc, char **argv)
{
int size, rank;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
printf("Hi dear, I am printing from rank %d\n",rank);
double a, b, c;
MPI_Status status, status2;
if (rank == 0)
{
a = 10.1;
MPI_Send(&a,1,MPI_DOUBLE,1,99,MPI_COMM_WORLD);
}
if (rank == 1)
{
b = 20.1;
MPI_Recv(&a,1,MPI_DOUBLE,0,99,MPI_COMM_WORLD,&status);
c = a + b;
printf("\nThe value of c is %f \n",c);
}
MPI_Sendrecv(&c,1,MPI_DOUBLE,2,100,
&c,1,MPI_DOUBLE,1,100,MPI_COMM_WORLD,&status2);
MPI_Barrier(MPI_COMM_WORLD);
if(rank == 2)
{
printf("\n Printing from rank %d, c is %f\n",rank, c);
}
MPI_Finalize();
return 0;
When a process calls MPI_Sendrecv, it will always try to execute both the send and receive part. For instance, if it is process 2, it will not look at the dest (fourth argument), see a "2" and think, "Oh, I don't have to do any sending. I'll just do the receive." nstead, process 2 will see the "2" and think, "Ah, I have to send something to myself." Further, as you have written this code, all processes see the MPI_SendRecv and think, "Oh. I have to send something to process 2 (the fourth argument) and receive something from process 1 (the ninth argument). So here we go ..." The problem is that process 1 isn't getting a command to send anything, so everyone, even process 1, is waiting for process 1 to send something.
MPI_Sendrecv is a very useful function. I find uses for it all the time. But it is intended for sending in a "chain". For instance, 0 sends to 1 while 1 sends to 2 while 2 sends to 0 or whatever. I think in this case you are better off with the usual MPI_Send and MPI_Recv.

MPI send recv confusion

I have attached a sample of the MPI program I am trying to write. When I run this program using "mpirun -np 4 a.out", my output is:
Sender: 1
Data received from 1
Sender: 2
Data received from 1
Sender: 2
And the run hangs there. I dont understand why does the sender variable change its value after MPI_recv? Any ideas?
Thank you,
Pradeep
` program mpi_test
include 'mpif.h'
!----------------( Initialize variables )--------------------
integer, dimension(3) :: recv, send
integer :: sender, np, rank, ierror
call mpi_init( ierror )
call mpi_comm_rank( mpi_comm_world, rank, ierror )
call mpi_comm_size( mpi_comm_world, np, ierror )
!----------------( Main program )--------------------
! receive the data from the other processors
if (rank.eq.0) then
do sender = 1, np-1
print *, "Sender: ", sender
call mpi_recv(recv, 3, mpi_int, sender, 1,
& mpi_comm_world, status, ierror)
print *, "Data received from ",sender
end do
end if
! send the data to the main processor
if (rank.ne.0) then
send(1) = 3
send(2) = 4
send(3) = 4
call mpi_send(send, 3, mpi_int, 0, 1, mpi_comm_world, ierr)
end if
!----------------( clean up )--------------------
call mpi_finalize(ierror)
return
end program mpi_test`
This is a typical stack smashing scenario. You have not declared the status variable and hence the compiler automatically makes one REAL variable for you. But status should rather be an INTEGER array of MPI_STATUS_SIZE elements:
integer, dimension(MPI_STATUS_SIZE) :: status
What happens in your case is that status is too small to hold the real MPI status object and hence some of the other stack variables get overwritten. Simply declare status as it should be declared in order to solve the problem.
Another thing - modern Fortran supports the IMPLICIT NONE statement, which disables automatic declaration of undeclared variables. If you put implicit none immediately after the include statement, the compiler would generate an error message instead.

MPI call and receive in a nested loop

I have a nested loop and from inside the loop I call the MPI send which I want it to
send to the receiver a specific value then at the receiver takes the data and again sends MPI messages
to another set of CPUs ... I used something like this but it looks like there is a problem in the receive ... and I cant see where I went wrong ..."the machine goes to infinite loop somewhere ...
I am trying to make it work like this :
master CPU >> send to other CPUs >> send to slave CPUs
.
.
.
int currentCombinationsCount;
int mp;
if (rank == 0)
{
for (int pr = 0; pr < combinationsSegmentSize; pr++)
{
int CblockBegin = CombinationsSegementsBegin[pr];
int CblockEnd = CombinationsSegementsEnd [pr];
currentCombinationsCount = numOfCombinationsEachLoop[pr];
prossessNum = 1; //specify which processor we are sending to
// now substitute and send to the main Processors
for (mp = CblockBegin; mp <= CblockEnd; mp++)
{
MPI_Send(&mp , 1, MPI_INT , prossessNum, TAG, MPI_COMM_WORLD);
prossessNum ++;
}
}//this loop goes through all the specified blocks for the combinations
} // end of rank 0
else if (rank > currentCombinationsCount)
{
// here I want to put other receives that will take values from the else below
}
else
{
MPI_Recv(&mp , 1, MPI_INT , 0, TAG, MPI_COMM_WORLD, &stat);
// the code stuck here in infinite loop
}
You've only initialised currentCombinationsCount within the if(rank==0) branch so all other procs will see an uninitialised variable. That will result in undefined behaviour and the outcome depends on your compiler. Your program may crash or the value may be set to 0 or an undetermined value.
If you're lucky, the value may be set to 0 in which case your branch reduces to:
if (rank == 0) { /* rank == 0 will enter this }
else if (rank > 0) { /* all other procs enter this }
else { /* never entered! Recvs are never called to match the sends */ }
You therefore end up with sends that are not matched by any receives. Since MPI_Send is potentially blocking, the sending proc may stall indefinitely. With procs blocking on sends, it can certainly look as thought "...the machine goes to infinite loop somewhere...".
If currentCombinationsCount is given an arbitrary value (instead of 0) then rank!=0 procs will enter arbitrary branchss (with a higher chance of all entering the final else). You then end up with second set of receives not being called resulting in the same issue as above.

Resources