C MPI中3D过程分解中用于交换2D晕的子阵列数据类型数
假设一个维度为C MPI中3D过程分解中用于交换2D晕的子阵列数据类型数,c,3d,parallel-processing,2d,mpi,C,3d,Parallel Processing,2d,Mpi,假设一个维度为GX*GY*GZ的全局立方体,该立方体使用3D笛卡尔拓扑分解为3D大小为PX*PY*PZ的立方体。为数据交换添加光环这将成为(PX+2)*(PY+2)*(PZ+2)。假设我们为2Dhalo交换使用子数组数据类型-我们需要定义12子数组类型吗 我的推理是:对于YZ平面,我们创建一个用于发送的子阵列类型和一个用于接收的子阵列类型,因为起始坐标将在子阵列数据类型本身中指定。但是存在2yz平面,这导致4子阵列数据类型。尽管全局和局部数据大小保持不变,但由于起始索引的原因,我们需要定义不同的
GX*GY*GZ
的全局立方体,该立方体使用3D
笛卡尔拓扑分解为3D
大小为PX*PY*PZ
的立方体。为数据交换添加光环这将成为(PX+2)*(PY+2)*(PZ+2)
。假设我们为2D
halo交换使用子数组数据类型-我们需要定义12
子数组类型吗
我的推理是:对于
YZ
平面,我们创建一个用于发送的子阵列类型和一个用于接收的子阵列类型,因为起始坐标将在子阵列数据类型本身中指定。但是存在2yz
平面,这导致4
子阵列数据类型。尽管全局和局部数据大小保持不变,但由于起始索引的原因,我们需要定义不同的子数组类型。使用矢量数据类型发送其中四个平面,使用子阵列数据类型发送其余两个平面,不是更好吗 这里有三种数据访问模式-发送/接收子域的X面、Y面和Z面-因此需要三种不同的方式来描述这些模式。您使用哪种类型以及使用多少类型来描述,这在很大程度上取决于您找到的表达和使用这些模式的最清晰方式
假设你有,局部,PX=8,PY=5,PZ=7,所以包括光晕,局部子域是10x7x9。这是在C中,因此我们假设数据存储在某个连续数组中,arr[ix][iy][iz]
,因此当前值(ix,iy,1)和(ix,iy,2)是连续的(偏移一个项目大小-例如双字节8个字节),值(ix,1,iz)和(ix,2,iz)偏移(PZ+2)[也就是说,9]值,(1,iy,iz)和(2,iy,iz)偏移(PY 2)*PZ+2)[=7*9=63]个值
让我们看看这是怎么回事,画出网格的面,z/y是左/右和上/下,x显示在相邻的面板中。为了简单起见,我们将在发送/接收的内容中包含角单元格
将y面发送到上邻居所需的数据如下所示:
x = 0 x = 1 ... x = 9 Local Grid Size:
+---------+ +---------+ +---------+ PX = 8
6 | | | | | | PY = 5
5 |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| PZ = 7
4 ^| | ^| | ^| |
3 || | || | || |
2 y| | y| | y| |
1 | | | | | |
0 | | | | | |
+---------+ +---------+ +---------+
012345678 012345678 ... 012345678
z-> z-> z->
也就是说,它将从[0][PY][0](例如,[0][5][0])开始,并扩展到[PX+1][PY][PZ+1]。因此,您将从[0][PY 0]…[0][PY PZ][PZ+1]开始,这是PZ+2连续值,然后转到[1][PY 0]——这是[0][PY 0]的(PY 2)*(PZ+2)值的跳跃,并取另一个PZ+2连续值,依此类推。您可以简单地表示为:
- 计数PX+2、blocklen(PZ+2)和步长(PY+2)*(PZ+2)的MPI_类型_向量,或
- MPI_Type_子阵列,切片子基为[PX+2,1,PZ+2],从[0,PY,0]开始
现在,让我们考虑接收这些数据:
x = 0 x = 1 ... x = 9 Local Grid Size:
+---------+ +---------+ +---------+ PX = 8
6 | | | | | | PY = 5
5 | | | | | | PZ = 7
4 ^| | ^| | ^| |
3 || | || | || |
2 y| | y| | y| |
1 | | | | | |
0 |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@|
+---------+ +---------+ +---------+
012345678 012345678 ... 012345678
z-> z-> z->
关键的是,所需的数据模式完全相同:PZ+2值,然后从最后一个块开始跳过(PY+2)*(PZ+2)值,以及另一个PZ+2值。我们可以将其描述为:
- 计数PX+2、blocklen(PZ+2)和步长(PY+2)*(PZ+2)的MPI_类型_向量,或
- MPI_Type_子阵列,片子基为[PX+2,1,PZ+2],从[0,0,0]开始
MPI_Type_create_subarray(ndims=3, sizes=[PX+2,PY+2,PZ+2], subsizes=[PX+2,1,PZ+2],
starts=[0,0,0],... &recv_down_yface_t);
MPI_Type_create_subarray(...all the same...
starts=[0,1,0],... &send_down_yface_t);
MPI_Type_create_subarray(...all the same...
starts=[0,PY,0],... &send_up_yface_t);
MPI_Type_create_subarray(...all the same...
starts=[0,PY+1,0],... &recv_up_yface_t);
/* Send lower yface */
MPI_Send(&(arr[0][0][0]), 1, send_down_yface_t, ... );
/* Send upper yface */
MPI_Send(&(arr[0][0][0]), 1, send_up_yface_t, ... );
/* Receive lower face */
MPI_Recv(&(arr[0][0][0]), 1, recv_down_yface_t, ... );
/* Receive upper face */
MPI_Recv(&(arr[0][0][0]), 1, recv_up_yface_t, ... );
它声明了四个具有不同起点的等效模式,您也可以定义一个,并将其用于指向所需数据的不同起点:
MPI_Type_create_subarray(ndims=3, sizes=[PX+2,PY+2,PZ+2], subsizes=[PX+2,1,PZ+2],
starts=[0,0,0],... &yface_t);
/* ... */
/* Send lower yface */
MPI_Send(&(arr[0][1][0]), 1, yface_t, ... );
/* Send upper yface */
MPI_Send(&(arr[0][PY][0]), 1, yface_t, ... );
/* Receive lower face */
MPI_Recv(&(arr[0][0][0]), 1, yface_t, ... );
/* Receive upper face */
MPI_Recv(&(arr[0][PY+1][0]), 1, yface_t, ... );
以上就是使用相应向量类型的方法——将其指向要发送/接收的第一个项
如果您选择使用子阵列类型,任何一种使用方式都是非常好的,您将在不同的软件中看到这两种选择。这只是一个您可以更清晰地选择的问题-每个模式4种类型(取决于偏移量),或者在发送/接收中显式地使用偏移量。我个人认为1-type方法更清晰,但是对于这个问题没有明确的正确答案
至于是使用MPI_子阵列还是矢量(例如),最简单的方法是查看您需要支持的其他两种模式:使用X面(这里您有更多的选项,因为它们是连续的:
- (PY+2)*(PZ+2)MPI_双打
- 1个MPI_类型(PY+2)*(PZ+2)MPI_双打
- 计数1、分块(PY+2)*(PZ+2)和任意步长的MPI_类型_向量,或计数PY+2、分块PZ+2和PZ+2步长的MPI_类型_向量,或任何等效组合
- 子阵列,切片子基为[1,PY+2,PZ+2],从适当的位置开始
- 计数(PX+2)*(PY+2)、blocklen 1和PZ+2步幅的MPI_类型_向量
- 一个子阵,其切片子基为[PX+2,PY+2,1],从适当的位置开始
所以,这又一次归结为清晰性。子数组类型在所有方向上看起来最相似,区别也很明显;而如果我向你展示了一组向量类型,它们都在同一段代码中声明,你必须在白板上做一些草图,以确保我没有意外地切换它们。子数组ray也最容易推广——如果你使用一种现在每边都需要2个halo单元的方法,比如说,或者不发送角单元,那么对子数组的修改是微不足道的,而你必须做一些工作来构建向量。Petsc的
DM
结构可能代表一个三维规则数组data那个