【问题标题】:MPI_Sendrecv deadlocks on 3+ processes超过 3 个进程的 MPI_Sendrecv 死锁
【发布时间】:2011-06-28 20:20:56
【问题描述】:

试图进行“光环/幽灵”行交换,但我被一个死锁难住了(在图片下方的代码 sn-p 中)。 要交换的“光环”行表示为深灰色线(在图片中)和hp[0]hp[M-1](在代码中)。

[不能发图片;声望不够。再次用文字说明:hp[0]hp[M-1] 是“晕”行(即要交换的行),而 hp[1]hp[M-2](以及中间的所有行)将被计算。]

为什么这个 sn-p(对 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_Barriers,因为我随机尝试哪种组合会起作用(谈论“黑匣子”)。因此,除了那些MPI_Barriers(和循环前的MPI_Sccaterv)之外,没有任何其他通信正在进行。出于测试目的,我在循环之后的MPI_Gatherv 之前执行return 0;(因此这也应该没有死锁影响)。

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 退出循环......这会/可能导致死锁(以及其他我不知道的可能情况)? 如果是这样,如何防止它?

关于“奇怪”tags 的另一件事。我刚刚运行了上面的循环,所有MPI_Barriers 都被注释掉了......并得到了这个“奇怪”(有一个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

【问题讨论】:

    标签: c mpi parallel-processing


    【解决方案1】:

    还有其他的,我们只是最近活跃的...

    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?所以这一定来自当前代码 sn-p 之外的另一个通信阶段。

    根据我在队列中看到的内容猜测,我猜代码类似于:

    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;
    

    【讨论】:

    • 乔纳森,谢谢您的回复。错误的标签确实很奇怪(尤其是tag=2),因为没有其他通信阶段(除了MPI_Barriers;请参阅带有完整循环的更新问题)。 [可悲的是,缺乏“15 声望”我无法投票给你。]
    • 啊,好吧,我仍然不知道 tag=2 发生了什么,但是是的,如果你可以让一个进程在其他进程之前跳出循环,你肯定会得到死锁,因为至少有一个“循环内”进程将永远卡住,等待来自永远不会到来的“循环外”进程的接收。
    • 您将如何防止这种情况发生(一个进程中断并让其他所有人陷入僵局)?或者这是一个设计缺陷,我应该以某种方式(如何?)重新排列代码?我真的坚持这个。
    • 看看上面的代码更新到答案;仅当 每个人的 差异小于 epsilon 时,您才希望进程离开,否则您必须继续迭代。
    • 美丽...MPI_Allreduce 的用法如此简洁自然!谢谢乔纳森!现在,我还将尝试从您的其他答案中找到如何MPI_Scatterv 2D(现在只有 1D)数组的块......因此避免笨拙(而且昂贵) 在上面的代码中将 2D 分解为 1D 然后返回到 2D hp[][]。如果我不“明白”,预计会再次被打扰。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多