MPI不能通过MPI_Send和MPI_Recv向自己发送数据

时间:2013-11-21 21:43:33

标签: mpi openmpi

我正在尝试实施MPI_Bcast,我计划通过MPI_SendMPI_Recv来实现这一点,但似乎我无法向自己发送消息?

代码如下

void My_MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm) {
     int comm_rank, comm_size, i;
     MPI_Comm_rank(comm, &comm_rank);
     MPI_Comm_size(comm, &comm_size);
     if(comm_rank==root){
         for(i = 0; i < comm_size; i++){
                 MPI_Send(buffer, count, datatype, i, 0, comm);
         }
     }
     MPI_Recv(buffer, count, datatype, root, 0, comm, MPI_STATUS_IGNORE);
  }

有什么建议吗?或者我永远不应该向自己发送信息而只是做一个记忆复制?

4 个答案:

答案 0 :(得分:4)

您的程序在多个级别上都是错误的。首先,条件中存在错误:

if(comm_rank=root){

这不会将comm_rankroot进行比较,而是将root分配给comm_rank,然后只有在root非零时才会执行循环除此之外,所有职级都会执行。

其次,根进程不需要将数据发送给自己,因为数据已经存在。即使你想要发送和接收,你应该注意到MPI_SendMPI_Recv都使用相同的缓冲区空间,这是不正确的。一些MPI实现使用直接内存副本进行自我交互,即库可能使用memcpy()来传输消息。使用带有重叠缓冲区的memcpy()(包括使用相同的缓冲区)会导致未定义的行为。

实施线性广播的正确方法是:

void My_MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
{
   int comm_rank, comm_size, i;
   MPI_Comm_rank(comm, &comm_rank);
   MPI_Comm_size(comm, &comm_size);
   if (comm_rank == root)
   {
      for (i = 0; i < comm_size; i++)
      {
         if (i != comm_rank)
            MPI_Send(buffer, count, datatype, i, 0, comm);
      }
   }
   else
      MPI_Recv(buffer, count, datatype, root, 0, comm, MPI_STATUS_IGNORE);
}

流程在没有死锁的情况下与自己交谈的常用方法是:

  • 结合使用MPI_IsendMPI_RecvMPI_SendMPI_Irecv的组合;
  • 使用缓冲发送MPI_Bsend;
  • 使用MPI_SendrecvMPI_Sendrecv_replace

MPI_IrecvMPI_Send的组合适用于在像您这样的循环中完成多个发送的情况。例如:

MPI_Request req;

// Start a non-blocking receive
MPI_Irecv(buff2, count, datatype, root, 0, comm, &req);
// Send to everyone
for (i = 0; i < comm_size; i++)
   MPI_Send(buff1, count, datatype, i, 0, comm);
// Complete the non-blocking receive
MPI_Wait(&req, MPI_STATUS_IGNORE);

请注意使用单独的缓冲区进行发送和接收。可能唯一的点对点MPI通信调用允许相同的缓冲区用于发送和接收MPI_Sendrecv_replace以及集体MPI调用的就地模式。但这些内部实现的方式是,在任何时候都不会使用相同的内存区域进行发送和接收。

答案 1 :(得分:2)

这是一个不正确的程序。你不能依靠对自己进行阻止MPI_Send ...因为它可能会阻止。在缓冲区再次可用之前,MPI不保证您的MPI_Send返回。在某些情况下,这可能意味着它将阻塞,直到目的地收到消息。在您的程序中,目标可能永远不会调用MPI_Recv,因为它仍在尝试发送。

现在,在My_MPI_Bcast示例中,根进程已经拥有了数据。为什么需要发送或复制它?

答案 2 :(得分:1)

根节点上的MPI_Send / MPI_Recv块可能是死锁。

转换为MPI_Isend可用于解决此问题。但是,可能存在问题,因为发送缓冲区正在被重用,并且root很可能“早期”到达MPI_Recv,然后可能在将其传输到其他级别之前更改该缓冲区。这对大型工作尤其有用。此外,如果从fortran调用此例程,则每个MPI_Send调用上的缓冲区可能会出现问题。

MPI_Sendrecv的使用只能用于根进程。这将允许在根进程进入专用MPI_Sendrecv之前,所有非根等级的MPI_Send“完成”(例如,可以安全地改变发送缓冲区)。 for循环只是以“1”而不是“0”开头,并且MPI_Sendrecv调用添加到该循环的底部。 (为什么这是一个更好的问题,因为数据在“缓冲区”中并且将“缓冲”。)

然而,这一切都引出了一个问题,为什么你要这样做呢?如果这是一个简单的“学术练习”,用点对点电话写一个集体,那就这样吧。但是,你的方法充其量是天真的。在任何合理实施的mpi中,任何MPI_Bcast算法都会击败此总体策略。

答案 3 :(得分:-1)

我认为你应该只为MPI_Recv(buffer, count, datatype, root, 0, comm, MPI_STATUS_IGNORE);添加rank=root,否则它可能会挂起