【问题标题】:MPI Send Recv deadlockMPI 发送接收死锁
【发布时间】:2017-03-04 07:00:05
【问题描述】:

我使用 MPI 编写程序,并且可以访问两个不同的集群。我不擅长系统管理,所以我对那里使用的软件、操作系统、编译器一无所知。但是,在一台机器上,我使用这样的代码遇到了死锁:

#include "mpi.h"
#include <iostream>

int main(int argc, char **argv) {

  int rank, numprocs;
  MPI_Status status;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

  int x = rank;

  if (rank == 0) {
      for (int i=0; i<numprocs; ++i)
          MPI_Send(&x, 1, MPI_INT, i, 100500, MPI_COMM_WORLD);
  }
  MPI_Recv(&x, 1, MPI_INT, 0, 100500, MPI_COMM_WORLD, &status);

  MPI_Finalize();
  return 0;
}

错误信息是相关的:

Fatal error in MPI_Send: Other MPI error, error stack:
MPI_Send(184): MPI_Send(buf=0x7fffffffceb0, count=1, MPI_INT, dest=0, tag=100500, MPI_COMM_WORLD) failed
MPID_Send(54): DEADLOCK: attempting to send a message to the local process without a prior matching receive

为什么会这样?我不明白,为什么它会发生在一台机器上,而不会发生在另一台机器上?

【问题讨论】:

    标签: multithreading mpi deadlock


    【解决方案1】:

    MPI_Send 是一个阻塞操作。在发布匹配的接收之前,它可能无法完成。在您的情况下,rank 0 在发布匹配接收之前尝试向自己发送消息。如果你必须做这样的事情,你可以在接收后用 MPI_Isend(+MPI_Wait...` 替换 MPI_Send)。但是你最好不要让他给自己发消息。

    在您的情况下正确使用的是MPI_Bcast

    【讨论】:

    • 谢谢祖兰,但在我的情况下,我不能使用 MPI_Bcast(相关代码只是我现实生活中的一个例子)。
    • MPI 提供了许多不同的集合操作。在适用的情况下,通常首选使用那些。我建议您展示一个更接近应用程序通信模式的示例。
    【解决方案2】:

    由于等级 0 已经具有正确的值 x,因此您无需在消息中发送它。这意味着在循环中您应该跳过发送到排名 0 而是从排名 1 开始:

    if (rank == 0) {
        for (int i=1; i<numprocs; ++i)
            MPI_Send(&x, 1, MPI_INT, i, 100500, MPI_COMM_WORLD);
    }
    MPI_Recv(&x, 1, MPI_INT, 0, 100500, MPI_COMM_WORLD, &status);
    

    现在 rank 0 不会尝试与自己对话,但由于接收在条件之外,它仍然会尝试从自己接收消息。解决方案是简单地使接收替代分支:

    if (rank == 0) {
        for (int i=1; i<numprocs; ++i)
            MPI_Send(&x, 1, MPI_INT, i, 100500, MPI_COMM_WORLD);
    }
    else
        MPI_Recv(&x, 1, MPI_INT, 0, 100500, MPI_COMM_WORLD, &status);
    

    另一个更复杂的解决方案是使用非阻塞操作在发送操作之前发布接收:

    MPI_Request req;
    
    MPI_Irecv(&x, 1, MPI_INT, 0, 100500, MPI_COMM_WORLD, &req);
    if (rank == 0) {
        int xx = x;
        for (int i=0; i<numprocs; ++i)
            MPI_Send(&xx, 1, MPI_INT, i, 100500, MPI_COMM_WORLD);
    }
    MPI_Wait(&req, &status);
    

    现在排名 0 不会在 MPI_Send 中阻塞,因为之前已经发布了匹配的接收。在所有其他等级中,MPI_Irecv 将紧跟在MPI_Wait 之后,这相当于阻塞接收 (MPI_Recv)。请注意,x 的值被复制到条件内的不同变量中,因为出于明显的正确性原因,MPI 标准禁止同时从同一内存位置发送和接收。

    【讨论】:

      猜你喜欢
      • 2016-06-15
      • 2016-10-03
      • 2018-03-12
      • 2013-12-25
      • 2018-12-04
      • 2013-04-15
      • 1970-01-01
      • 2012-08-24
      • 2018-07-21
      相关资源
      最近更新 更多