具有异步非阻塞MPI的死锁

时间:2018-08-01 10:10:57

标签: mpi

我试图弄清楚为什么以下代码偶尔会导致死锁。在2进程上运行,每个进程启动与另一个进程的异步发送和接收。但是,两个进程的调用次数不同(num_iters)。为了匹配未完成的消息,在for循环结束时,每个进程都会检查发送或接收是否正在进行,如果没有,则启动匹配消息。最后,每个进程都等待匹配的通信。为什么代码偶尔会死锁? (已经通过OpenMPI和MPICH进行了测试)

  MPI_Init(&argc, &argv);

  int procs;
  MPI_Comm_size(MPI_COMM_WORLD, &procs);
  if (procs != 2) {
    std::cout << "Runs with 2 processes!";
    MPI_Abort(MPI_COMM_WORLD, 1);
  }

  int rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  int buffer_size = 10;
  std::vector<double> snd_buffer(buffer_size, 4.2);
  std::vector<double> rcv_buffer(buffer_size, 0);

  MPI_Request snd_req = MPI_REQUEST_NULL;
  int snd_flag;

  MPI_Request rcv_req = MPI_REQUEST_NULL;
  int rcv_flag;

  // Asynchronously spam messages
  int comm_rank = (rank + 1) % 2;
  int num_iters = (rank == 0) ? 10 : 20;
  for (int i = 0; i < num_iters; ++i) {
    MPI_Test(&snd_req, &snd_flag, MPI_STATUS_IGNORE);
    if (snd_flag != 0) {
      MPI_Issend(snd_buffer.data(), buffer_size, MPI_INT, comm_rank, 0, MPI_COMM_WORLD, &snd_req);
    }

    MPI_Test(&rcv_req, &rcv_flag, MPI_STATUS_IGNORE);
    if (rcv_flag != 0) {
      MPI_Irecv(rcv_buffer.data(), buffer_size, MPI_INT, comm_rank, 0, MPI_COMM_WORLD, &rcv_req);
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(50)); // simulate work
  }

  MPI_Barrier(MPI_COMM_WORLD);

  // Match outstanding messages
  MPI_Test(&snd_req, &snd_flag, MPI_STATUS_IGNORE);
  if (snd_flag != 0) {
    MPI_Issend(snd_buffer.data(), buffer_size, MPI_INT, comm_rank, 0, MPI_COMM_WORLD, &snd_req);
  }

  MPI_Test(&rcv_req, &rcv_flag, MPI_STATUS_IGNORE);
  if (rcv_flag != 0) {
    MPI_Irecv(rcv_buffer.data(), buffer_size, MPI_INT, comm_rank, 0, MPI_COMM_WORLD, &rcv_req);
  }

  MPI_Wait(&rcv_req, MPI_STATUS_IGNORE);
  MPI_Wait(&snd_req, MPI_STATUS_IGNORE);

  MPI_Finalize();

1 个答案:

答案 0 :(得分:0)

我认为您误解了MPI_Test的作用和不的作用。它不会检查是否有未解决的消息(即待处理的传入消息)。 (您可以使用MPI_IProbeMPI_Test来做到这一点,但这并不能真正帮助您)。相反,MPI_Test检查本地操作是否已完成。之所以可以这样进行,是因为在空请求句柄上的MPI_Test确实将标志设置为true。

还请注意,同步模式(MPI_Issend)不能保证接收已完成,而只能保证已过帐接收。

在一次迭代中,您可以完成发送,但尚未完成接收,或者可以完成另一种方式。因此,发布的请求的对称性没有任何可以保证死锁自由的不变性。实际的死锁交互过程有些复杂,但是您可以很容易地添加一些输出来查看发生的情况。

处理消息数量互不相同的情况的通常方法是发送经过特殊标记的消息,表明这是最后一条消息(即循环已完成)。您可以使用消息中的数据或特殊标签。可以保证在同一通信伙伴之间不会出现超车消息,从而为您提供帮助。