0

私は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ポイントで待機します。彼らが終わったとき、それは動きます....誰かが私のものが動かない理由を教えてもらえますか?また、私は「タグ」の議論についてあまり明確ではありません(手がかり?)事前にすべてのあなたの助けに感謝します!!!

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

ほとんど問題なく、隣人を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_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);

ここでいくつかのポイント:

  • 異なる方向への通信に個別のタグを使用する必要はありません。元の質問のように混乱を招くだけです。
  • MPI_ISEND/MPI_IRECVスキームを概説したように のシーケンスに置き換えるとMPI_SENDRECV、ハロー スワップを 2D、3D、および anyD のケースに簡単に拡張できます。もちろん、ノンブロッキング センドを使用することもできますが、 で順次実行するとMPI_SENDRECV、対角要素もそれぞれのハローに自動的に移動します (つまり、2D で左上のローカル要素を左上の対角線の隣のハローに移動します)。 .
于 2013-03-26T11:53:05.720 に答える