2013-12-08 84 views
5

我正在试验MPI,并且如果此代码可能导致死锁,它正在游荡。与MPI的死锁

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status); 
} else if (my_rank == 1) { 
    MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status); 
} 

回答

9

MPI_Send可能会或可能不会阻止。它将阻塞,直到发件人可以重新使用发件人缓冲区。当缓冲区已发送到较低的通信层时,某些实现将返回给调用者。当另一端有匹配的MPI_Recv()时,其他人将返回给呼叫者。所以这取决于你的MPI实现,无论这个程序是否会死锁。

因为这个程序的行为会有所不同不同的MPI实现中,你可以考虑rewritting会这样就不会有可能出现的死锁:

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status); 
} else if (my_rank == 1) { 
    MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status); 
    MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm); 
} 

永远知道,每MPI_Send()必须有一个配对MPI_Recv(),在时间上“并行”。例如,这可能会导致死锁,因为配对send/recv调用没有及时对齐。他们相互交叉:

RANK 0       RANK 1 
----------      ------- 
MPI_Send() ---   ---- MPI_Send() | 
       ---  ---     | 
       ------      | 
        --       | TIME 
       ------      | 
       ---  ---     | 
MPI_Recv() <--   ---> MPI_Recv() v 

这些过程,在其他的方式,不会结束僵局,前提当然,确实有两个进程在同一个通信域等级0和1。如果通信com的大小不允许秩1(只有0)

RANK 0       RANK 1 
----------      ------- 
MPI_Send() ------------------> MPI_Recv() | 
              | TIME 
              | 
MPI_Recv() <------------------ MPI_Send() v 

上面固定程序可能会失败。这样,if-else将不采取else路由,因此,没有进程将监听MPI_Send()和秩0将死锁。

如果您需要使用当前的通信布局,那么您可能更愿意使用MPI_Isend()MPI_Issend()代替非阻塞发送,从而避免死锁。

3

@mcleod_ideafix的帖子非常好。我想添加更多有关非阻塞MPI调用的内容。

大多数MPI实现的方式是将数据从用户缓冲区复制到其他位置。它可能是实施内部的缓冲区,在正确的网络类型上它可能更好。当这些数据被复制出用户缓冲区并且缓冲区可以被应用程序重新使用时,MPI_SEND调用返回。这可能在匹配MPI_RECV被调用之前,或者可能不匹配。您发送的数据越大,在拨打MPI_RECV之前,您的消息越有可能被阻止。

避免这种情况的最好方法是使用非阻塞呼叫MPI_IRECVMPI_ISEND。这样,您可以先发帖MPI_IRECV,然后拨打电话MPI_ISEND。当消息到达时(这是因为缓冲区来保存它们已经可以通过MPI_IRECV),这可以避免额外的副本,从而使事情变得更快,并且避免了死锁情况。因此,现在您的代码如下所示:

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Irecv (recvbuf, count, MPI_INT, 1, tag, comm, &status, &requests[0]); 
    MPI_Isend (sendbuf, count, MPI_INT, 1, tag, comm, &requests[1]); 
} else if (my_rank == 1) { 
    MPI_Irecv (recvbuf, count, MPI_INT, 0, tag, comm, &status, &requests[0]); 
    MPI_Isend (sendbuf, count, MPI_INT, 0, tag, comm, &requests[1]); 
} 
MPI_Waitall(2, request, &statuses); 
0

由于mcleod_ideafix解释了您的代码可能导致死锁。 在这里你去:Explanation and two possible issue Solutions, one by rearranging execution order, one by async send recv calls

继承人与异步的需要解决:

if (rank == 0) { 
     MPI_Isend(..., 1, tag, MPI_COMM_WORLD, &req); 
     MPI_Recv(..., 1, tag, MPI_COMM_WORLD, &status); 
     MPI_Wait(&req, &status); 
} else if (rank == 1) { 
     MPI_Recv(..., 0, tag, MPI_COMM_WORLD, &status); 
     MPI_Send(..., 0, tag, MPI_COMM_WORLD); 
}