MPI_Sendrecv在3个进程上发生死锁

时间:2011-06-27 18:47:51

标签: c mpi parallel-processing

尝试进行“晕/鬼”行交换,我遇到了死锁(在图片下方的代码片段中)。 要交换的“光晕”行表示为深灰色线(在图片中)以及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?!

PS:请原谅我,如果我要求(并且遗漏)明显的东西,但我已经阅读了太多的SO问题,唉,没有找到解决方案。这么多,我知道Jonathan Dursi,High Performance Mark和suszterpatt的HPC三人组。

更新(完整循环)

循环没有更多,所以我可以完整地发布它:它有一些评论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

1 个答案:

答案 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;
相关问题