我使用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
等级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);
然后排名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需要其他时间来接收真实数据吗?或其他一些原因?
答案 0 :(得分:6)
这里一遍又一遍地讨论了非阻塞操作的使用。从MPI规范(非阻塞通信部分):
同样,非阻塞接收启动调用会启动接收操作,但不会完成。在将消息存储到接收缓冲区之前,可以返回该调用。需要单独的接收完成调用来完成接收操作并验证数据是否已接收到接收缓冲区中。 使用合适的硬件,将数据传输到接收器存储器可以在接收启动之后和完成之前完成计算。
(粗体文字从标准中逐字复制; 斜体的重点是我的)
关键句是最后一句。除非MPI_WAIT[ALL|SOME|ANY]
或MPI_TEST[ALL|SOME|ANY]
被调用(MPI_TEST*
设置值为true,否则该标准不保证非阻塞接收操作将完成(甚至开始)完成标志)。
默认情况下,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;
如果您希望同时处理多个工作进程,则需要更多参与:
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
中,已完成的请求的索引将填入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);
MPI_Isend
后紧跟MPI_Wait
等效至MPI_Send
,以下代码完全有效(且更易于理解):
// Receiver
MPI_Irecv(..., &request);
... do something ...
MPI_Wait(&request, &status);
// Sender
MPI_Send(...);