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->nx,g->nyg->nz是该进程所持有的数组块的大小,g->west, g->east, g->north, g->south,g->upg->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]

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

谁能告诉我我是否正确地进行了沟通?特别是向量类型的定义。我在代码中定义的向量类型会提取 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->

因此,您希望发送 count=(ny*nz) 1 个值的块,每个块都跨步为 nx。我假设这里 nx、ny 和 nz 包括保护单元,并且您正在发送角值。如果您不发送角值,则子数组是要走的路。至关重要的是,我还假设 g->data 是 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);

请注意,我们发送的总数为 count*blocksize = ny*nz 值,这是正确的,并且我们正在跨越进程中的 count*stride = nx*ny*nz 内存,这也是正确的。

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

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

所以你有 nz 个 nx 值块,每个块都由步幅 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,并且您有两个保护单元(两侧各一个),则您的左侧保护单元为 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 回答