1

大規模な 3D グリッドで計算を行い、MPI を使用して機能するようにハロー交換手順を使用するコードを書いています。コードから間違った結果が得られます。これは、ハロー交換が適切に機能していないことが原因であると確信しています。

基本的に、私は大きな 3D 配列を持っており、そのチャンクは各プロセスに保持されています。各プロセスには、保持しているデータのチャンクよりも各次元で 2 要素大きい配列があります。これにより、配列の残りの部分に格納されているデータに影響を与えることなく、配列の各面にハロー交換を行うことができます。ハロー交換通信を行う次のコードがあります。

  MPI_Type_vector(g->ny, g->nx, g->nx, MPI_DOUBLE, &face1);
  MPI_Type_commit(&face1);

  MPI_Type_vector(2*g->ny, 1, g->nx, MPI_DOUBLE, &face2);
  MPI_Type_commit(&face2);

  MPI_Type_vector(g->nz, g->nx, g->nx * g->ny, MPI_DOUBLE, &face3);
  MPI_Type_commit(&face3);

  /* Send to WEST receive from EAST */
  MPI_Sendrecv(&(g->data)[current][0][0][0], 1, face1, g->west, tag,
    &(g->data)[current][0][0][0], 1, face1, g->east, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

  /* Send to EAST receive from WEST */
  MPI_Sendrecv(&(g->data)[current][g->nz-1][0][0], 1, face1, g->east, tag,
    &(g->data)[current][g->nz-1][0][0], 1, face1, g->west, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);



  /* Send to NORTH receive from SOUTH */
  MPI_Sendrecv(&(g->data)[current][0][0][0], 1, face2, g->north, tag,
    &(g->data)[current][0][0][0], 1, face2, g->south, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

  /* Send to SOUTH receive from NORTH */
  MPI_Sendrecv(&(g->data)[current][0][g->ny-1][0], 1, face2, g->south, tag,
    &(g->data)[current][0][0][0], 1, face2, g->north, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);



  /* Send to UP receive from DOWN */
  MPI_Sendrecv(&(g->data)[current][0][0][0], 1, face3, g->up, tag,
    &(g->data)[current][0][0][0], 1, face3, g->down, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

  /* Send to DOWN receive from UP */
  MPI_Sendrecv(&(g->data)[current][0][0][g->nx-1], 1, face3, g->down, tag,
    &(g->data)[current][0][0][g->nx-1], 1, face3, g->up, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

g->nxg->nyおよびg->nzは、このプロセスが保持している配列チャンクのサイズでありg->westg->eastg->northg->southg->upおよびg->downは、次のコードを使用して検出された各方向の隣接プロセスのランクです。

/* Who are my neighbours in each direction? */
  MPI_Cart_shift( cart_comm, 2, 1, &g->north, &g->south);
  MPI_Cart_shift( cart_comm, 1, 1, &g->west, &g->east);
  MPI_Cart_shift( cart_comm, 0, 1, &g->up, &g->down);

各プロセスの配列は次のように定義されます。

array[2][g->nz][g->ny][g->nx]

(ハロー交換を行ったら、更新ルーチンを介して毎回更新する必要があるため、2 つのコピーがあります)。

私がコミュニケーションを正しく行っているかどうか、誰か教えてもらえますか? 特にベクトル型の定義。コードで定義したベクター型は、3D 配列の各面を抽出しますか? そして、MPI_Sendrecv 呼び出しは正しく見えますか?

なぜ私のコードが機能しないのか完全に理解できませんでしたが、通信に関連していることは確かです。

4

1 に答える 1

3

そのため、私はMPI_Type_create_subarrayを使用して配列のスライスを引き出すのが大好きです。ベクトル型よりもまっすぐにしておく方が簡単です。一般に、複数のガードセルを記述するために単一のベクター型を使用することはできません (複数のストライドがあるため、ベクターのベクターを作成する必要があります) が、ここでは各方向に 1 つのガードセルしか使用していないためだと思います。君は大丈夫だよ。

それでは、x-face GC について考えてみましょう。ここでは、yz 平面全体を x 隣人に送信しています。メモリ内では、配列レイアウトを考えると、これは次のようになります。

 +---------+
 |        @|
 |        @|
 |        @|
 |        @|   z=2
 |        @|
 +---------+
 |        @|
 |        @|
 |        @|   z=1
 |        @|
 |        @|
 +---------+
 |        @|
^|        @|
||        @|   z=0
y|        @|
 |        @|
 +---------+
    x->

したがって、1 つの値の count=(ny*nz) ブロックを、それぞれ nx ずつ送信しようとしています。ここでは、nx、ny、および nz にガードセルが含まれており、コーナーの値を送信していると想定しています。コーナー値を送信していない場合は、部分配列が適しています。また、重要なことに、g->data は連続したnx*ny*nz*2 ブロック (または nx*ny*nz の 2 つの連続したブロック) の double であると仮定しています。そうでなければ、すべてが失われます。

したがって、タイプの作成は次のようになります

MPI_Type_vector((g->ny*g->nz), 1, g->nx, MPI_DOUBLE, &face1);
MPI_Type_commit(&face1);

count*blocksize = ny*nz の値の合計を送信していることに注意してください。これは正しく、プロセス内で count*stride = nx*ny*nz メモリをまたいでいます。これも正しいです。

わかりましたので、y 面は次のようになります。

 +---------+
 |@@@@@@@@@|
 |         |
 |         |
 |         |   z=2
 |         |
 +---------+
 |@@@@@@@@@|
 |         |
 |         |   z=1
 |         |
 |         |
 +---------+
 |@@@@@@@@@|
^|         |
||         |   z=0
y|         |
 |         |
 +---------+
    x->

したがって、nx 値の nz 個のブロックがあり、それぞれがストライド nx*ny で区切られています。したがって、タイプの作成は次のようになります

MPI_Type_vector(g->nz, g->nx, (g->nx)*(g->ny), MPI_DOUBLE, &face2);
MPI_Type_commit(&face2);

もう一度再確認すると、count*blocksize = nz*nx 値を送信し、count*stride = nx*ny*nz メモリをまたいでいます。小切手。

最後に、z 面データの送信には、xy 平面全体の送信が含まれます。

 +---------+
 |@@@@@@@@@|
 |@@@@@@@@@|
 |@@@@@@@@@|   z=2
 |@@@@@@@@@|
 |@@@@@@@@@|
 +---------+
 |         |
 |         |
 |         |   z=1
 |         |
 |         |
 +---------+
 |         |
^|         |
||         |   z=0
y|         |
 |         |
 +---------+
    x->

MPI_Type_vector(1, (g->nx)*(g->ny), 1, MPI_DOUBLE, &face3);
MPI_Type_commit(&face3);

もう一度再確認すると、count*blocksize = nx*ny 値を送信し、count*stride = nx*ny メモリをまたいでいます。小切手。

更新

私はあなたの Sendrecvs を見ていませんでしたが、そこにも何かがあるかもしれません。ベクター データ型で送信する最初のデータへのポインターを使用する必要があることに注意してください。

まず、x 方向の配列サイズが nx で、2 つのガードセル (両側に 1 つ) がある場合、左のガードセルは 0、右のガードセルは nx-1、「実際の」データは 1..nx から拡張されます。 -2. したがって、西端のデータを西隣に送信し、東隣から東端のガードセルに受信するには、次のようにします。

 /* Send to WEST receive from EAST */
 MPI_Sendrecv(&(g->data)[current][0][0][g->nx-2], 1, face1, g->west, westtag,
              &(g->data)[current][0][0][0], 1, face1, g->east, westtag, 
              MPI_COMM_WORLD, MPI_STATUS_IGNORE);

 /* Send to EAST receive from WEST */
 MPI_Sendrecv(&(g->data)[current][0][0][1], 1, face1, g->east, easttag,
              &(g->data)[current][0][0][g->nx-1], 1, face1, g->west, easttag, 
              MPI_COMM_WORLD, MPI_STATUS_IGNORE);

(コミュニケーションの段階ごとに異なるタグを使用するのが好きで、物事を整理しておくのに役立ちます。)

他の方向についても同様です。

于 2011-05-16T03:09:04.443 に答える