【问题标题】:MPI Non-blocking Irecv didn't receive data?MPI Non-blocking Irecv 没有收到数据?
【发布时间】:2014-05-14 14:56:45
【问题描述】:

我使用MPI非阻塞通信(MPI_Irecv, MP_Isend)来监控slave的空闲状态,代码如下。

排名 0:

int dest = -1;
while( dest <= 0){
   int i;
   for(i=1;i<=slaves_num;i++){
      printf("slave %d, now is %d \n",i,idle_node[i]);
      if (idle_node[i]== 1) {
         idle_node[i] = 0;
         dest = i;
         break;
      }
   }
   if(dest <= 0){
      MPI_Irecv(&idle_node[1],1,MPI_INT,1,MSG_IDLE,MPI_COMM_WORLD,&request);
      MPI_Irecv(&idle_node[2],1,MPI_INT,2,MSG_IDLE,MPI_COMM_WORLD,&request);
      MPI_Irecv(&idle_node[3],1,MPI_INT,3,MSG_IDLE,MPI_COMM_WORLD,&request);
      // MPI_Wait(&request,&status);
   }
   usleep(100000);
}

idle_node[dest] = 0;//indicates this slave is busy now

排名 1、2、3:

while(1)
{
   ...//do something
   MPI_Isend(&idle,1,MPI_INT,0,MSG_IDLE,MPI_COMM_WORLD,&request);
   MPI_Wait(&request,&status);
}

它有效,但我希望它更快,所以我删除了该行:

usleep(100000);

那么 rank 0 就这样进入死机状态:

slave 1, now is 0
slave 2, now is 0
slave 3, now is 0 
slave 1, now is 0
slave 2, now is 0
slave 3, now is 0 
...

那么是不是表示我使用MPI_Irecv时只是告诉MPI我想在这里接收消息(还没有收到消息),而MPI需要其他时间才能接收到真实的数据?还是其他原因?

【问题讨论】:

    标签: nonblocking openmpi


    【解决方案1】:

    这里已经反复讨论了非阻塞操作的使用。来自 MPI 规范(非阻塞通信部分):

    类似地,一个非阻塞的receive start call会启动接收操作,但不会完成它。该调用可以在消息存储到接收缓冲区之前返回。需要一个单独的 receive complete 调用来完成接收操作并验证数据是否已被接收到接收缓冲区中。 使用合适的硬件,数据到接收器内存的传输可以与在接收开始之后和完成之前完成的计算同时进行。

    粗体是从标准中逐字复制的;斜体中的重点是我的)

    关键句是最后一句。除非调用MPI_WAIT[ALL|SOME|ANY]MPI_TEST[ALL|SOME|ANY]MPI_TEST* 将完成标志的值设置为 true),否则该标准不保证非阻塞接收操作将永远完成(甚至开始)。

    默认情况下,Open MPI 作为单线程库提供,并且没有特殊的硬件加速,进行非阻塞操作的唯一方法是定期调用一些非阻塞调用(以MPI_TEST* 为主要示例)或调用一个阻塞的(主要示例是MPI_WAIT*)。

    此外,您的代码会导致严重的泄漏,迟早会导致资源耗尽:您使用相同的 request 变量多次调用 MPI_Irecv,有效地覆盖了它的值并丢失了对先前启动的请求的引用.未被等待的请求永远不会被释放,因此会保留在内存中。

    在您的情况下绝对不需要使用非阻塞操作。如果我正确理解逻辑,您可以使用以下代码实现您想要的:

    MPI_Recv(&dummy, 1, MPI_INT, MPI_ANY_SOURCE, MSG_IDLE, MPI_COMM_WORLD, &status);
    idle_node[status.MPI_SOURCE] = 0;
    

    如果您想同时处理多个工作进程,则涉及更多:

    MPI_Request reqs[slaves_num];
    int indices[slaves_num], num_completed;
    
    for (i = 0; i < slaves_num; i++)
       reqs[i] = MPI_REQUEST_NULL;
    
    while (1)
    {
       // Repost all completed (or never started) receives
       for (i = 1; i <= slaves_num; i++)
          if (reqs[i-1] == MPI_REQUEST_NULL)
             MPI_Irecv(&idle_node[i], 1, MPI_INT, i, MSG_IDLE,
                       MPI_COMM_WORLD, &reqs[i-1]);
    
       MPI_Waitsome(slaves_num, reqs, &num_completed, indices, MPI_STATUSES_IGNORE);
    
       // Examine num_completed and indices and feed the workers with data
       ...
    }
    

    在调用MPI_Waitsome 之后,将会有一个或多个已完成的请求。确切的数字将在num_completed 中,并且已完成请求的索引将填充在indices[] 的第一个num_completed 元素中。完成的请求将被释放,reqs[] 的对应元素将被设置为MPI_REQUEST_NULL

    此外,关于使用非阻塞操作似乎存在一个常见的误解。非阻塞发送可以与阻塞接收匹配,阻塞发送也可以与非阻塞接收同等匹配。这使得这种结构变得毫无意义:

    // Receiver
    MPI_Irecv(..., &request);
    ... do something ...
    MPI_Wait(&request, &status);
    
    // Sender
    MPI_Isend(..., &request);
    MPI_Wait(&request, MPI_STATUS_IGNORE);
    

    MPI_Isend 紧跟MPI_WaitMPI_Send等效,以下代码完全有效(并且更易于理解):

    // Receiver
    MPI_Irecv(..., &request);
    ... do something ...
    MPI_Wait(&request, &status);
    
    // Sender
    MPI_Send(...);
    

    【讨论】:

    • 感谢您的回复,您说的完全正确。这对像我这样的新渔民有很大帮助。根据您的第一个建议,使用 MPI_Recv() 接收来自工作进程的消息,这是否意味着 recv 方法的 mount 和 mount fo send 方法不相等?
    • 阻塞接收必须匹配相同的发送次数,否则如果发送次数较少,接收将无限期挂起。
    • 没错。关于你的回答,我认为MPI_Waitsome(slaves_num-1, reqs, &amp;num_completed, indices, MPI_STATUSES_IGNORE); 的第一个参数应该是 slaves_num (不需要减1)。不是什么大问题,它有效,:)。
    • 糟糕,当一个人尝试同时编写两个不同的代码版本时,就会发生这种情况。
    • 如果你使用了 MPI_Waital,那么你就不需要无限循环了吧?
    猜你喜欢
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 2012-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    相关资源
    最近更新 更多