【问题标题】:Deadlock with MPIMPI 死锁
【发布时间】:2013-12-25 05:50:51
【问题描述】:

我正在试验 MPI,想知道这段代码是否会导致死锁。

MPI_Comm_rank (comm, &my_rank);
if (my_rank == 0) {
   MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm);
   MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status);
} else if (my_rank == 1) {
   MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm);
   MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status);
}

【问题讨论】:

    标签: mpi deadlock


    【解决方案1】:

    MPI_Send 可能会或可能不会阻止。它将阻塞,直到发送方可以重用发送方缓冲区。当缓冲区被发送到较低的通信层时,一些实现将返回给调用者。当另一端有匹配的MPI_Recv() 时,其他一些将返回给调用者。所以这个程序是否会死锁取决于你的 MPI 实现。

    由于这个程序在不同的 MPI 实现中表现不同,你可以考虑重写它,这样就不会出现死锁:

    MPI_Comm_rank (comm, &my_rank);
    if (my_rank == 0) {
       MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm);
       MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status);
    } else if (my_rank == 1) {
       MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status);
       MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm);
    }
    

    请始终注意,对于每个 MPI_Send(),必须有一个配对 MPI_Recv(),两者在时间上“平行”。例如,这可能会以死锁告终,因为配对的发送/接收调用没有及时对齐。它们相互交叉:

    RANK 0                          RANK 1
    ----------                      -------
    MPI_Send() ---            ----  MPI_Send()    |
                  ---      ---                    |
                     ------                       |
                       --                         | TIME
                     ------                       |
                  ---      ---                    |
    MPI_Recv() <--            --->  MPI_Recv()    v
    

    另一方面,这些进程不会以死锁结束,当然前提是在同一个通信器域中确实有两个等级为 0 和 1 的进程。

    RANK 0                          RANK 1
    ----------                      -------
    MPI_Send() ------------------>  MPI_Recv()   |
                                                 | TIME
                                                 |
    MPI_Recv() <------------------  MPI_Send()   v
    

    如果通信器com 的大小不允许排名1(只有0),上述固定程序可能会失败。这样,if-else 将不会采用else 路由,因此,没有进程会监听MPI_Send(),并且等级 0 将死锁。

    如果您需要使用当前的通信布局,那么您可能更愿意使用MPI_Isend()MPI_Issend() 来代替非阻塞发送,从而避免死锁。

    【讨论】:

      【解决方案2】:

      @mcleod_ideafix 的帖子非常好。我想添加更多关于非阻塞 MPI 调用的内容。

      大多数 MPI 实现的方式是将数据从用户缓冲区复制到其他地方。它可能是实现内部的缓冲区,在正确的网络上可能会更好。当该数据被复制出用户缓冲区并且应用程序可以重用该缓冲区时,MPI_SEND 调用将返回。这可能是在调用匹配的MPI_RECV 之前,也可能不是。您发送的数据越大,您的消息就越有可能在调用MPI_RECV 之前被阻塞。

      避免这种情况的最佳方法是使用非阻塞调用MPI_IRECVMPI_ISEND。这样您就可以先发布您的MPI_IRECV,然后致电MPI_ISEND。这避免了消息到达时的额外副本(因为保存它们的缓冲区已经通过MPI_IRECV 可用),这使事情变得更快,并且避免了死锁情况。所以现在你的代码看起来像这样:

      MPI_Comm_rank (comm, &my_rank);
      if (my_rank == 0) {
         MPI_Irecv (recvbuf, count, MPI_INT, 1, tag, comm, &status, &requests[0]);
         MPI_Isend (sendbuf, count, MPI_INT, 1, tag, comm, &requests[1]);
      } else if (my_rank == 1) {
         MPI_Irecv (recvbuf, count, MPI_INT, 0, tag, comm, &status, &requests[0]);
         MPI_Isend (sendbuf, count, MPI_INT, 0, tag, comm, &requests[1]);
      }
      MPI_Waitall(2, request, &statuses);
      

      【讨论】:

        【解决方案3】:

        正如 mcleod_ideafix 解释的那样,您的代码可能会导致死锁。 给你:Explanation and two possible issue Solutions, one by rearranging execution order, one by async send recv calls

        这是异步调用的解决方案:

        if (rank == 0) {
                MPI_Isend(..., 1, tag, MPI_COMM_WORLD, &req);
                MPI_Recv(..., 1, tag, MPI_COMM_WORLD, &status);
                MPI_Wait(&req, &status);
        } else if (rank == 1) {
                MPI_Recv(..., 0, tag, MPI_COMM_WORLD, &status);
                MPI_Send(..., 0, tag, MPI_COMM_WORLD);
        }
        

        【讨论】:

          猜你喜欢
          • 2019-05-29
          • 2017-03-04
          • 1970-01-01
          • 1970-01-01
          • 2016-06-15
          • 2016-10-03
          • 2014-04-04
          • 1970-01-01
          • 2023-03-29
          相关资源
          最近更新 更多