0

我是 MPI 的新手,所以请放轻松...无论如何,我正在尝试使用 MPI_Isend 和 MPI_Irecv 进行非阻塞通信。我编写了一个名为“halo_exchange”的子程序,每次我需要在相邻子域之间交换光环单元时都想调用它。我能够正确地拆分域,并且我知道我的每个邻居等级。在下面的代码中,邻居是北/南的(即我使用一维行分解)。所有过程都用于计算。也就是说,所有的进程都会调用这个子程序,并且需要交换数据。

最初我对北边界和南边界使用一组 MPI_Isend/MPI_Irecv 调用,但后来我把它分开,认为将“MPI_PROC_NULL”传递给函数可能有问题(边界不是周期性的)。这就是 if 语句的原因。代码继续挂在“MPI_Waital”语句上,我不知道为什么?它实际上只是在等待,我不确定它在等待什么?

#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_Waital 点等待相应的通信发生。当他们完成后,它会继续前进....有人能告诉我为什么我的不动吗???另外我对“标签”论点不太清楚(线索?)提前感谢您的所有帮助!

4

2 回答 2

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);

大部分都很好,你不应该ifMPI_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_Recvetc. 已经是指向数据的指针,如:

  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]);
于 2013-03-23T15:23:00.797 回答
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);

这里有几点:

  • 您不需要为不同方向的通信使用单独的标签。它只会像您原来的问题一样导致混乱。
  • Replacing your MPI_ISEND/MPI_IRECV scheme with the sequence of MPI_SENDRECV as outlined allows you to easily extend the halo swap to 2D, 3D and anyD cases. Of course you can still use non-blocking sends, but if done sequentially with MPI_SENDRECV, it automagically moves the diagonal elements to the respective halo too (i.e. it moves the topmost leftmost local element to the halo of the top-left diagonal neighbour in 2D).
于 2013-03-26T11:53:05.720 回答