我是MPI的新手,所以对我很轻松......无论如何,我正在尝试使用MPI_Isend和MPI_Irecv进行非阻塞通信。我写了一个名为“halo_exchange”的子程序,我想在每次需要在相邻子域之间交换光环单元时调用它。我能够正确地分割域名,并且我知道我的每个邻居队伍。在下面的代码中,邻居定向为北/南(即我使用1D行分解)。所有过程都用于计算。换句话说,所有进程都将调用此子例程并需要交换数据。
最初我在北边界和南边界都使用了一组MPI_Isend / MPI_Irecv调用,但后来我把它分开,以为将“MPI_PROC_NULL”传递给函数可能有问题(边界不是周期性的)。这就是if语句的原因。代码继续挂在“MPI_Waitall”语句上,我不知道为什么?字面上只是等待,我不确定它在等什么?
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
//---------------------------------------------------------------------------------------
// FUNCTION "halo_exchange"
//---------------------------------------------------------------------------------------
void halo_exchange(PREFIX **array, MPI_Comm topology_comm, \
int nn, int S_neighbor, int N_neighbor)
{
int halo = 2;
int M = 20;
...
double *S_Recv,*N_Recv;
double *S_Send,*N_Send;
// Receive buffers
S_Recv = (double *) calloc( M*halo,sizeof(double) );
N_Recv = (double *) calloc( M*halo,sizeof(double) );
// Send buffers
S_Send = (double *) calloc( M*halo,sizeof(double) );
N_Send = (double *) calloc( M*halo,sizeof(double) );
...
// send buffers filled with data
// recv buffers filled with zeros (is this ok...or do I need to use malloc?)
...
if (S_neighbor == MPI_PROC_NULL)
{
MPI_Status status[2];
MPI_Request req[2];
MPI_Isend(&N_Send,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[0]);
MPI_Irecv(&N_Recv,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[1]);
...
...
MPI_Waitall(2,req,status);
}
else if (N_neighbor == MPI_PROC_NULL)
{
MPI_Status status[2];
MPI_Request req[2];
MPI_Isend(&S_Send,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[0]);
MPI_Irecv(&S_Recv,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[1]);
...
...
MPI_Waitall(2,req,status);
}
else
{
MPI_Status status[4];
MPI_Request req[4];
MPI_Isend(&S_Send,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[0]);
MPI_Isend(&N_Send,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[1]);
MPI_Irecv(&N_Recv,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[2]);
MPI_Irecv(&S_Recv,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[3]);
...
...
MPI_Waitall(4,req,status);
}
...
}
这是我最初的理解,显然缺少一些东西: 由于每个进程都调用此子例程,因此将调用所有send / recv函数。然后,所有进程将在其MPI_Waitall点等待相应的通信发生。当他们完成它继续前进....有人可以告诉我为什么我的不动?另外我对“标签”论点不太清楚(线索?)感谢你提前帮助!!!
答案 0 :(得分:3)
这段代码
MPI_Status status[4];
MPI_Request req[4];
MPI_Isend(&S_Send,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[0]);
MPI_Isend(&N_Send,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[1]);
MPI_Irecv(&N_Recv,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[2]);
MPI_Irecv(&S_Recv,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[3]);
...
...
MPI_Waitall(4,req,status);
大部分都很好,你不应该在if
邻居附近MPI_PROC_NULL
;这就是MPI_PROC_NULL
的用途,因此您可以将角落案例推入MPI例程,从而大大简化面向开发人员的通信代码。
这里的问题实际上是标签。标签附加到单个邮件。标签可以是任何非负整数,最大可达一定,但关键是发送方和接收方必须就标签达成一致。
如果您向北方邻居发送带有标签2的一些数据,那很好,但现在假装您是北方邻居;您将从您的南方邻居收到标记为2 的相同消息。同样,如果您要使用标记1发送南邻居数据,则该南邻居将需要从其北邻居使用标记1接收该邻居数据。
所以你真的想要
MPI_Isend(&S_Send,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[0]);
MPI_Isend(&N_Send,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[1]);
MPI_Irecv(&N_Recv,halo*M,MPI_DOUBLE,N_neighbor,1,topology_comm,&req[2]);
MPI_Irecv(&S_Recv,halo*M,MPI_DOUBLE,S_neighbor,2,topology_comm,&req[3]);
根据以下OP评论进行更新:
事实上,由于S_Recv
等已经指向数据,因为:
S_Recv = (double *) calloc( M*halo,sizeof(double) );
你真正想要的是:
MPI_Isend(S_Send,halo*M,MPI_DOUBLE,S_neighbor,1,topology_comm,&req[0]);
MPI_Isend(N_Send,halo*M,MPI_DOUBLE,N_neighbor,2,topology_comm,&req[1]);
MPI_Irecv(N_Recv,halo*M,MPI_DOUBLE,N_neighbor,1,topology_comm,&req[2]);
MPI_Irecv(S_Recv,halo*M,MPI_DOUBLE,S_neighbor,2,topology_comm,&req[3]);
答案 1 :(得分:1)
除了正确获取标签之外,您还可以进一步改进代码。您使用非阻塞操作实现的数据通信操作非常常见,MPI提供了自己的调用 - MPI_SENDRECV
。有了它,您的代码将简化为:
MPI_Sendrecv(&S_Send, halo*M, MPI_DOUBLE, S_neighbor, 0,
&N_Recv, halo*M, MPI_DOUBLE, N_neighbor, 0,
topology_comm, MPI_STATUS_IGNORE);
MPI_Sendrecv(&N_Send, halo*M, MPI_DOUBLE, N_neighbor, 0,
&S_Recv, halo*M, MPI_DOUBLE, S_neighbor, 0,
topology_comm, MPI_STATUS_IGNORE);
这里有几点:
MPI_ISEND
顺序替换MPI_IRECV
/ MPI_SENDRECV
方案,可以轻松地将光环交换扩展为2D,3D和anyD情况。当然你仍然可以使用非阻塞发送,但如果按顺序使用MPI_SENDRECV
,它会自动将对角线元素移动到相应的光环(即它将最左边的本地元素移动到左上角的光环2D中的对角邻居。