我正在尝试使用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);
}
答案 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_IRECV
和MPI_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);
}