尝试进行“晕/鬼”行交换,我遇到了死锁(在图片下方的代码片段中)。
要交换的“光晕”行表示为深灰色线(在图片中)以及hp[0]
和hp[M-1]
(在代码中)。
[无法发布图片;没有足够的声誉。再说一次:hp[0]
和hp[M-1]
是“halo”行(即要交换的行),而hp[1]
和hp[M-2]
(以及中间的所有行)都是用。计算。
为什么这个代码段(2个进程可以正常工作)与3个以上的进程发生死锁?
// in-between processes ("P1" and "P2" in the picture;
// one of "P1" and "P2" is of course missing in the case of 3 processes)
if (p > 0 && p < P-1)
{
MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0,
hp[0], N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);
MPI_Sendrecv(hp[1], N, MPI_DOUBLE, p-1, 1,
hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);
}
// root process ("P0" in the picture)
else if (p == 0)
{
MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0,
hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);
}
// last process ("P3" in the picture)
else
{
MPI_Sendrecv(hp[1], N, MPI_DOUBLE, p-1, 1,
hp[0], N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);
}
平台:带有DeinoMPI GUI的Windows XP,其按钮"Show Messages"
"Interrupts the running job and prints the current state of the message queues"
嗯,这是“当前状态”的示例(处于死锁状态时):
Rank 0 queues:
Posted receive queue:
rank=2, tag=1, context_id=1(Collective), count=0, dtype=MPI_BYTE
Rank 1 queues:
Posted receive queue:
rank=0, tag=0, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE
Received but unmatched queue:
rank=2, tag=2, context_id=MPI_COMM_WORLD, length=80
rank=2, tag=2, context_id=MPI_COMM_WORLD, length=80
rank=0, tag=1, context_id=1(Collective), length=0
Rank 2 queues:
Posted receive queue:
rank=1, tag=1, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE
为什么有MPI_BYTE
作为数据类型而1(Collective)
作为上下文?为什么Rank 0
在他的接收队列中有rank = 2
?!
循环没有更多,所以我可以完整地发布它:它有一些评论MPI_Barrier因为我随机尝试哪种组合可行(谈论“黑匣子”) 。因此,除了那些MPI_Barrier
s(和循环之前的MPI_Sccaterv
)之外,没有任何其他通信正在进行。出于测试的目的,我在循环之后return 0;
之前做MPI_Gatherv
(所以这也应该没有死锁的含义)。
while (1)
{
difference = 0.0;
//MPI_Barrier(MPI_COMM_WORLD);
// in-between processes ("P1" and "P2" in the picture;
// one of "P1" and "P2" is of course missing in the case of 3 processes)
if (p > 0 && p < P-1)
{
MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0,
hp[0], N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);
MPI_Sendrecv(hp[1], N, MPI_DOUBLE, p-1, 1,
hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);
}
// root process ("P0" in the picture)
else if (p == 0)
{
MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, p+1, 0,
hp[M-1], N, MPI_DOUBLE, p+1, 1, MPI_COMM_WORLD, &s);
}
// last process ("P3" in the picture)
else
{
MPI_Sendrecv(hp[1], N, MPI_DOUBLE, p-1, 1,
hp[0], N, MPI_DOUBLE, p-1, 0, MPI_COMM_WORLD, &s);
}
//MPI_Barrier(MPI_COMM_WORLD);
// calculate "hpNEW" for each inner point
for (y = 1; y < M-1; ++y)
for (x = 1; x < N-1; ++x)
{
hpNEW[y][x] = (hp[y][x-1] + hp[y][x+1] + hp[y-1][x] + hp[y+1][x]) / 4.0;
if (fabs( hpNEW[y][x] - hp[y][x] ) > diff)
difference = fabs(hpNEW[y][x] - hp[y][x]);
}
if (difference < EPSILON)
break;
// transfer "hpNEW"'s calculated inner points to "hp" for next iteration
for (y = 1; y < M-1; ++y)
for (x = 1; x < N-1; ++x)
hp[y][x] = hpNEW[y][x];
} // while END
一个进程确实会break
首先退出循环...这会导致死锁(在我不知道的其他可能的事情中)吗?如果是这样,如何预防它?
关于“怪异”tag
的另一件事。我刚刚运行了上面的循环,所有MPI_Barrier
被注释掉了......并且得到了这个“奇怪的”(有一个tag=4
!)消息队列状态:
Rank 0 queues:
Posted receive queue:
rank=1, tag=4, context_id=1(Collective), count=30, dtype=MPI_DOUBLE
Received but unmatched queue:
rank=2, tag=1, context_id=1(Collective), length=0
Rank 1 queues:
Posted receive queue:
rank=0, tag=0, context_id=MPI_COMM_WORLD, count=10, dtype=MPI_DOUBLE
Received but unmatched queue:
rank=2, tag=1, context_id=MPI_COMM_WORLD, length=80
Rank 2 queues:
Posted receive queue:
rank=1, tag=1, context_id=1(Collective), count=0, dtype=MPI_BYTE
答案 0 :(得分:5)
还有其他人,我们只是最近活跃的人......
关于Windows上的DeinoMPI很有意思,我没有意识到它有很好的工具来实时查看正在发生的事情。
所以你绝对不会问一些明显的事情;从表面上看,我发现您发布的代码没有任何问题。我个人觉得使用类似MPI_PROC_NULL
的东西来简化代码逻辑更清楚:
left = p-1;
if (left < 0) left = MPI_PROC_NULL;
right = p+1;
if (right >= P) right = MPI_PROC_NULL;
MPI_Sendrecv(hp[M-2], N, MPI_DOUBLE, right, 0,
hp[0], N, MPI_DOUBLE, left , 0, MPI_COMM_WORLD, &s);
MPI_Sendrecv(hp[1], N, MPI_DOUBLE, left , 1,
hp[M-1], N, MPI_DOUBLE, right, 1, MPI_COMM_WORLD, &s);
让MPI库处理边缘情况,而不是使用显式测试if (p == 0)
等;但这是一个关于品味的问题,以及之后您将如何处理代码。
消息队列中的情况令人困惑,以至于我认为您发布的代码不会导致死锁,尽管它可能是(比如)排名1最终显示死锁的地方 - 它可能是其中排名1挂起。
如果你看看发生了什么,等级1等待等级0的10个双打,等级2等待等级1的10个双打,所以它就像你的光环填充的向右发送阶段 - 1和2已经发布了他们各自的收据 - 除了2的标签是错误的,它正在接收10个标签1的双打,这不应该发生(通过上面的代码)。
除此之外,等级0正在等待该集合完成(与之相关的零数据 - 可能是屏障?或MPI_Finalize或隐含同步的其他内容?)因此不会发送到1 ;等级1已经有一个消息作为该集体的一部分,所以如果它完成它将立即清除,并将其作为该集体的一部分。它还有两个消息已经从排名2开始,标签2?所以这必须来自当前代码片段之外的另一个通信阶段。
只是猜测我在队列中看到的内容,我猜测代码就像:
loop {
communication as posted above;
another phase of communication;
synchronization (barrier?)
}
这是第二阶段的沟通,有一个微妙的错误。
<强>更新强>:
好的,所以在不同时间退出循环的进程肯定会导致锁定,因为进程开始等待永远不会来自邻居的消息。但这很容易解决;在您本地计算出最大差异之后,您会发现处理器之间的差异最大值为MPI_Allreduce
;只有当hp和hpNEW之间的全局差异到处都小于EPSILON时才会继续。
// calculate "hpNEW" for each inner point locally
for (y = 1; y < M-1; ++y)
for (x = 1; x < N-1; ++x)
{
hpNEW[y][x] = (hp[y][x-1] + hp[y][x+1] + hp[y-1][x] + hp[y+1][x]) / 4.0;
if (fabs( hpNEW[y][x] - hp[y][x] ) > diff)
diff = fabs(hpNEW[y][x] - hp[y][x]);
}
// find the maximum of all the local differences
MPI_Allreduce (&diff, &globaldiff, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
if (globaldiff < EPSILON)
break;