Gau*_*ena 4 c 3d parallel-processing 2d mpi
假设一个全局的维度立方体,GX*GY*GZ
使用3D
笛卡尔拓扑将其分解为每个过程中3D
的大小多维数据集PX*PY*PZ
.添加Halos以便交换数据(PX+2)*(PY+2)*(PZ+2)
.假设我们使用Subarray数据类型进行2D
光环交换 - 我们是否需要定义12
子数组类型?
我的理由是:对于YZ
平面,我们创建一个Subarray类型用于发送,一个子阵列类型用于接收,因为起始坐标将在子阵列数据类型本身中指定.但是有2 YZ
平面,导致4
Subarray数据类型.虽然全局和本地数据大小保持不变但由于起始索引 - 我们需要定义4
不同的子阵列类型.使用Vector数据类型发送其中四个平面是不是更好,其余两个使用Subarray数据类型?
这里有三个数据访问模式 - 发送/接收子域的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-face所需的数据如下所示:
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->
Run Code Online (Sandbox Code Playgroud)
也就是说,它将从[0] [PY] [0]开始(例如,[0] [5] [0])并延伸到[PX + 1] [PY] [PZ + 1].所以你从[0] [PY] [0] ...... [0] [PY] [PZ + 1]开始,它们是PZ + 2个连续值,然后转到[1] [PY] [0 ] - 从[0] [PY] [0]跳转(PY + 2)*(PZ + 2)值,开始更早,并取另一个PZ + 2连续值,依此类推.你可以简单地表达为:
它们完全相同,并没有性能差异.
现在,让我们考虑接收这些数据:
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->
Run Code Online (Sandbox Code Playgroud)
至关重要的是,所需的数据模式完全相同:PZ + 2值,然后从最后一个块的开头跳过(PY + 2)*(PZ + 2)值,以及另一个PZ + 2值.我们可以将其描述为:
唯一的区别是子阵列类型的子阵列的起始位置.但这并不像看起来那么大!
当您在发送或接收(例如)中实际使用子数组类型时,将例程指针传递给某些数据,然后为其提供具有一些起始位置和切片描述的子数组类型.然后MPI跳到该起始位置,并使用该切片描述的数据布局.
因此,虽然定义和使用四种子阵列类型非常好:
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, ... );
Run Code Online (Sandbox Code Playgroud)
它声明了四个具有不同起点的等效模式,您也可以只定义一个,并使用它指向您需要的数据的不同起点:
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, ... );
Run Code Online (Sandbox Code Playgroud)
以上就是您使用相应矢量类型的方式 - 将其指向要发送/接收的第一个项目.
如果您选择使用子阵列类型,那么使用它的任何一种方式都非常好,您将看到在各种软件中做出的两种选择.这只是你更清楚的问题 - 每种模式有4种类型(取决于偏移),或者在发送/接收中明确使用偏移.我个人认为1类方法更加清晰,但对于这个问题没有明确的正确答案.
至于是否使用MPI_Subarray或Vector(比如说),最简单的方法是看看你需要支持的其他两种模式:使用X面(这里有更多选项,因为它们是连续的:
对于z-faces:
所以,这一切都归结为清晰.子阵列类型在所有方向之间看起来最相似,差异相当明显; 而如果我向你展示了一堆所有在同一段代码中声明的矢量类型,你必须在白板上做一些素描,以确保我没有意外地切换它们.子阵列也最容易推广 - 如果你转向一个方法,现在每边需要2个晕圈细胞,或者不发送角落细胞,对子阵列的修改是微不足道的,而你必须做一些工作用向量构建东西.
归档时间: |
|
查看次数: |
452 次 |
最近记录: |