MPI非阻塞Irecv没有收到数据?

Jer*_*rry 2 nonblocking openmpi

我使用MPI非阻塞通信(MPI_Irecv,MP_Isend)来监视从属的空闲状态,代码如下所示.

等级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
Run Code Online (Sandbox Code Playgroud)

等级1,2,3:

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

它工作,但我希望它更快,所以我删除该行:

usleep(100000);
Run Code Online (Sandbox Code Playgroud)

然后排名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 
...
Run Code Online (Sandbox Code Playgroud)

那么它是否表示当我使用它时MPI_Irecv,它只告诉MPI我想在这里收到一条消息(没有收到消息),而MPI需要其他时间来接收真实数据?还是其他一些原因?

Hri*_*iev 8

这里一遍又一遍地讨论了非阻塞操作的使用.从MPI规范(非阻塞通信部分):

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

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

关键句是最后一句.该标准不提供任何保证无阻塞接收操作永远不会完成(甚至开始),除非MPI_WAIT[ALL|SOME|ANY]MPI_TEST[ALL|SOME|ANY]作为(与MPI_TEST*为完成标志设置的真实的值).

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

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

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

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

如果您想同时处理多个工作进程,则需要更多参与:

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
   ...
}
Run Code Online (Sandbox Code Playgroud)

在呼叫之后MPI_Waitsome将有一个或多个已完成的请求.确切的数字将在,num_completed并且已完成的请求的索引将填入第一个num_completed元素indices[].已完成的请求将被释放,相应的元素reqs[]将被设置为MPI_REQUEST_NULL.

此外,似乎存在关于使用非阻塞操作的常见误解.阻塞接收可以匹配非阻塞发送,并且阻塞发送也可以由非阻塞接收同等地匹配.这使得这样的结构荒谬:

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

// Sender
MPI_Isend(..., &request);
MPI_Wait(&request, MPI_STATUS_IGNORE);
Run Code Online (Sandbox Code Playgroud)

MPI_Isend紧随其后的MPI_Wait等效的,MPI_Send以下代码完全有效(并且更容易理解):

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

// Sender
MPI_Send(...);
Run Code Online (Sandbox Code Playgroud)