与MPI的僵局

时间:2013-12-08 00:04:14

标签: mpi deadlock

我正在尝试使用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);
}

3 个答案:

答案 0 :(得分:13)

MPI_Send可能阻止也可能不阻止。它会阻塞,直到发送方可以重用发送方缓冲区。当缓冲区已发送到较低的通信层时,某些实现将返回调用方。当另一端有匹配的MPI_Recv()时,其他一些人将返回呼叫者。因此,无论该程序是否会死锁,都取决于您的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_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status);
   MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm);
}

始终要注意,对于每个MPI_Send(),必须有一个MPI_Recv()配对,两者都是“并行”的。例如,这可能以死锁结束,因为配对发送/接收呼叫未及时对齐。他们互相交叉:

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

另一方面,这些进程不会以死锁结束,当然,前提是在同一个通信器域中确实存在两个具有等级0和1的进程。

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

如果通信器com的大小不允许等级1(仅为0),则上述固定程序可能会失败。这样,if-else将不会采用else路由,因此,任何进程都不会监听MPI_Send(),而等级0将会死锁。

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

答案 1 :(得分:5)

@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);

答案 2 :(得分:2)

正如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);
}