Halo交换在MPI中无法正常工作

时间:2011-05-15 14:44:05

标签: c mpi parallel-processing hpc

我正在编写一些在大型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->nyg->nz是此流程所持有的数组块的大小,g->westg->east,{{1} },g->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);

(它有两个副本,因为我需要通过我的更新程序每次更新一次,一旦我完成了光环交换)。

有人能告诉我,我是否正确进行通讯?特别是矢量类型的定义。我在代码中定义的矢量类型是否会提取3D数组的每个面?并且MPI_Sendrecv调用看起来是否正确?

我完全迷失了为什么我的代码无效,但我很确定它与通信有关。

1 个答案:

答案 0 :(得分:3)

因此,我非常喜欢使用MPI_Type_create_subarray来取出数组切片;它比矢量类型更容易保持直线。一般来说,你不能使用单一的矢量类型来描述多维守卫(因为有多个步幅,你需要创建矢量矢量),但我认为因为你在这里每个方向只使用1个保护单元你还好。

让我们考虑一下x-face GC;在这里,您将整个y-z平面发送到您的x-neighbor。在内存中,给定您的数组布局,这看起来像这样:

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

因此,您希望发送1个值的count =(ny * nz)块,每个块都以nx为步长。我在这里假设nx,ny和nz包括guardcells,并且你正在发送角落值。如果您没有发送角落值,则可以选择子阵列。至关重要的是,我还假设g->数据是nx * ny * nz * 2的连续块(或nx * ny * nz的2个连续块)加倍,否则全部是丢失。

所以你的类型创建应该像

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

请注意,我们正在发送总计数* blocksize = ny * nz的值,这是正确的,我们正在跨过count * stride = nx * ny * nz内存,这也是正确的。

好的,所以y脸看起来像这样:

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

所以你有nz个nx值块,每个块用stride nx * ny分隔。所以你的类型创建应该看起来像

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

再次仔细检查,你发送count * blocksize = nz * nx值,跨步计数* stride = nx * ny * nz内存。检查。

最后,发送z-face数据涉及发送整个x-y平面:

 +---------+
 |@@@@@@@@@|
 |@@@@@@@@@|
 |@@@@@@@@@|   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值,striding count * stride = nx * ny memory。检查。

<强>更新

我没有看看你的Sendrecvs,但也可能有一些东西。请注意,您必须使用指向您使用矢量数据类型发送的第一个数据的指针。

首先,如果你在x方向上有数组大小nx,并且你有两个守护单元(两边各一个),你的左卫星单元是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);

(我喜欢为每个沟通阶段使用不同的标签,有助于保持排序。)

同样适用于其他方向。