MPI发送Recv死锁

时间:2016-10-21 21:17:00

标签: multithreading mpi deadlock

我使用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

为什么会这样?我无法理解,为什么它会在一台机器上发生,但不会在另一台机器上发生?

2 个答案:

答案 0 :(得分:1)

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

在您的案例中使用的正确方法是MPI_Bcast

答案 1 :(得分:1)

由于排名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);

现在排名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标准禁止同时发送和接收到同一内存位置。