2013-11-21 71 views
0

我正在尝试执行MPI_Bcast,我打算通过MPI_SendMPI_Recv来做到这一点,但似乎我无法向自己发送消息?MPI无法通过MPI_Send和MPI_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); 
    } 

上任何建议?或者我不应该发信息给自己,只是做一个记忆拷贝?

+0

在我的电脑上正常工作。你确定你没有混合指针/地址? – Sleepyhead

+1

@Sleepyhead,用大号“count”来试试。较小的消息通常被缓存或使用渴望的协议发送。 –

回答

0

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

1

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

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

2

根节点上的MPI_SEND/MPI_RECV块可以是一个死锁。

转换为MPI_Isend可以用来解决该问题。但是,可能会有问题,因为发送缓冲区正在被重用,并且root很可能会在“early”之前到达MPI_Recv,然后可能会在缓冲区传输到其他队列之前更改该缓冲区。这在大型工作中尤其可能。另外,如果这个程序是从Fortran中调用的,那么在每个MPI_Send调用中缓冲区可能会被破坏。

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

然而,这一切引出了一个问题,你为什么要在所有这样做呢?如果这是一个简单的“学术练习”,写一个集体点对点呼叫,那就这样吧。但是,你的方法最好是天真的。在任何合理实施的mpi中,这个总体策略将被任何MPI_Bcast算法击败。

+0

我只是遵循mpi教程,并尝试通过编程学习它。 – bxshi

3

你的程序在多个层面上是错误的。首先,有在有条件的错误:

if(comm_rank=root){ 

这并不比comm_rankroot而是分配rootcomm_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_Recv或组合的组合MPI_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呼叫的就地模式。但是这些内部实现方式使得在任何时候都不会使用相同的存储区域来发送和接收。

+0

感谢您的注意,我已经改变了它,以免由于简单的错误而使其他人感到困惑。 – bxshi