我正在编写一些在大型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->ny
和g->nz
是此流程所持有的数组块的大小,g->west
,g->east
,{{1} },g->north
,g->south
和g->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调用看起来是否正确?
我完全迷失了为什么我的代码无效,但我很确定它与通信有关。
答案 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);
(我喜欢为每个沟通阶段使用不同的标签,有助于保持排序。)
同样适用于其他方向。