繁体   English   中英

在 mpi 中处理计算和通信的过程

[英]process to process computation and communication in mpi

P1 P2
P3 P4

1 2 3 4
5 6 7 8
1 2 3 4
0 6 0 8

假设 P1,P2,P3,P4 是进程,P1 有数据点 1 2 5 6,P2 有数据点 3 4 7 8 P3 有数据点 1 2 0 6,P4 有数据点 3 4 0 8。我想执行 stecil对这条数据进行计算,使得新值 6 将是 averageof(2,5,7,2)。 但是 7 是 P2 的数据点,2 是 P3 的数据点。 如何解决这个问题? 我可以为单个进程运行它,但是如何通过正常的 MPI_Send 和 MPI_Recv 来解决这个问题? 还使用 type_contiguous 或 type_vector 和集体通信?

任何帮助将不胜感激。谢谢。

这个问题有一个众所周知的解决方案,称为幽灵细胞光晕 这个想法是根据模板用一层或多层额外的单元围绕每个子阵列。 在每次迭代开始时,每个进程通过与其最近的邻居交换数据来同步光环的 state,该操作称为光环交换 Halo 提供了计算所有内部单元格的新 state 的必要数据,但它们缺乏自己更新所需的数据,因此这些单元格的内容在一次迭代后变得“旧”,这就是为什么它们有时被称为“幽灵” .

这篇博文清楚地表达了这个想法,但使用的语言是 Julia。 由于 About 页面说可以在其他地方重用内容,所以我无耻地借用了以下插图:

幽灵细胞。取自 http://www.claudiobellei.com/2018/09/30/julia-mpi/

该图仅显示进程之间的边界上的光晕,但实际上,如果您在所有四个侧面都有光晕并且只更新需要的光晕,因为它使代码更加对称并降低了其复杂性,这会更容易。

您的特定情况下的分解如下:

1 2 3 4
5 6 7 8
1 2 3 4
0 6 0 8

首先变成

1 2 | 3 4
5 6 | 7 8
----+----
1 2 | 3 4
0 6 | 0 8

添加光环:

x x x x | x x x x
x 1 2 x | x 3 4 x
x 5 6 x | x 7 8 x
x x x x | x x x x
--------+--------
x x x x | x x x x
x 1 2 x | x 3 4 x
x 0 6 x | x 0 8 x
x x x x | x x x x

现在,在模板操作之前,您需要执行光环交换。 它按如下所示的步骤进行。 交换沿每个维度成对执行。 顺序并不重要。

1.1) 向东横向交换

每个进程将其最右边的列发送到其右侧的进程。 接收器将数据放在它的左晕列中:

x x [x] x | [x] x x x     x x [x] x | [x] x x x
x 1 [2] x | [x] 3 4 x     x 1 [2] x | [2] 3 4 x
x 5 [6] x | [x] 7 8 x     x 5 [6] x | [6] 7 8 x
x x [x] x | [x] x x x     x x [x] x | [x] x x x
----------+---------- --> ----------+----------
x x [x] x | [x] x x x     x x [x] x | [x] x x x
x 1 [2] x | [x] 3 4 x     x 1 [2] x | [2] 3 4 x
x 0 [6] x | [x] 0 8 x     x 0 [6] x | [6] 0 8 x
x x [x] x | [x] x x x     x x [x] x | [x] x x x

1.2) 向西横向交换

每个进程将其最左边的列发送到它左边的进程。 接收器将数据放在其右晕列中:

x x x [x] | x [x] x x     x x x [x] | x [x] x x
x 1 2 [x] | 2 [3] 4 x     x 1 2 [3] | 2 [3] 4 x
x 5 6 [x] | 6 [7] 8 x     x 5 6 [7] | 6 [7] 8 x
x x x [x] | x [x] x x     x x x [x] | x [x] x x
----------+---------- --> ----------+----------
x x x [x] | x [x] x x     x x x [x] | x [x] x x
x 1 2 [x] | 2 [3] 4 x     x 1 2 [3] | 2 [3] 4 x
x 0 6 [x] | 6 [0] 8 x     x 0 6 [0] | 6 [0] 8 x
x x x [x] | x [x] x x     x x x [x] | x [x] x x

2.1) 南向垂直交换

每个进程将其底行发送到它下面的进程。 接收器将数据放置在其顶部的光环行中:

[x x x x | x x x x]   [x x x x | x x x x]
 x 1 2 3 | 2 3 4 x     x 1 2 3 | 2 3 4 x
[x 5 6 7 | 6 7 8 x]   [x 5 6 7 | 6 7 8 x]
 x x x x | x x x x     x x x x | x x x x
 --------+-------- --> --------+--------
[x x x x | x x x x]   [x 5 6 7 | 6 7 8 x]
 x 1 2 3 | 2 3 4 x     x 1 2 3 | 2 3 4 x
[x 0 6 0 | 6 0 8 x]   [x 0 6 0 | 6 0 8 x]
 x x x x | x x x x     x x x x | x x x x

2.2) 向北垂直交换

每个进程将其顶行发送到它上面的进程。 接收器将数据放置在其底部的光环行中:

 x x x x | x x x x     x x x x | x x x x
[x 1 2 3 | 2 3 4 x]   [x 1 2 3 | 2 3 4 x]
 x 5 6 7 | 6 7 8 x     x 5 6 7 | 6 7 8 x
[x x x x | x x x x]   [x 1 2 3 | 2 3 4 x]
 --------+-------- --> --------+--------
 x 5 6 7 | 6 7 8 x     x 5 6 7 | 6 7 8 x
[x 1 2 3 | 2 3 4 x]   [x 1 2 3 | 2 3 4 x]
 x 0 6 0 | 6 0 8 x     x 0 6 0 | 6 0 8 x
[x x x x | x x x x]   [x x x x | x x x x]

这些操作中的每一个都可以通过单个 MPI 调用 - MPI_Sendrecv来实现。 发送部分发送本地数据行或列,接收部分将其接收到本地晕行或列中。

四次交换后的最终结果是:

x x x x | x x x x
x 1 2 3 | 2 3 4 x
x 5 6 7 | 6 7 8 x
x 1 2 3 | 2 3 4 x
--------+--------
x 5 6 7 | 6 7 8 x
x 1 2 3 | 2 3 4 x
x 0 6 0 | 6 0 8 x
x x x x | x x x x

您可能会注意到,即使每个光环中的角元素也包含人们期望存在的正确对角元素。 这样做的美妙之处在于,您可以简单地添加更多步骤以将其扩展到所需的任意维度,并且所有元素都会自动在光环区域中找到它们正确的 position。

您现在可以在本地获得所有 6 的所有四个 neigbhours,并且可以继续对它们的值进行平均。 请注意,您只使用光环中的值,而不是更新它们。

一些附加说明:

  1. 无论您是否有周期性边界条件,这都有效。 在周期性边界条件下,当进行右移时,P2 是 P1 的右邻居,P1 是 P2 的右邻居。 此外,P1 是 P2 的左邻居,P2 是 P1 的左邻居。 这同样适用于垂直方向。

  2. 您不需要特殊代码来处理边界上的进程。 如果一个进程没有右(或左、上或下)邻居,只需向/从MPI_PROC_NULL发送或接收消息。 即,代码如下:

     int right = compute_right_rank(); int left = compute_left_rank(); int up = compute_top_rank(); int down = compute_bottom_rank(); MPI_Sendrecv(right_column, 1, columndt, right, 0, left_halo, 1, columndt, left, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Sendrecv(left_column, 1, columndt, left, 0, right_halo, 1, columndt, right, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Sendrecv(bottom_row, 1, rowdt, down, 0, top_halo, 1, rowdt, up, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Sendrecv(top_column, 1, rowdt, up, 0, bottom_halo, 1, rowdt, down, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

    在这里,如果右边有排名, compute_right_rank()应该返回rank + 1 ,否则返回MPI_PROC_NULL 发送到MPI_PROC_NULL或从它接收是无操作的,即没有任何反应。 它允许您在没有if的情况下编写代码。

    columndt是一种 MPI 数据类型,对应于数组中的列。 您可以使用MPI_Type_vector构造它。 rowdt是一种 MPI 数据类型,表示数组中的一整行。 使用MPI_Type_contiguous构造它。

  3. 如果排名在笛卡尔通信器中,那么计算邻居的排名非常容易。 MPI_Cart_createMPI_Cart_shift是你最好的朋友。 您还可以使用 MPI 邻居集合来进一步减少 MPI 调用的数量。

  4. 由于边界条件不是周期性的,因此未填充底部等级的底部光晕。 最右边行列的右光晕、最上面行列的顶部光晕和最左边行列的左光晕的情况也是如此。 您可能希望使用特殊值预先填充它们,例如0 这个价值永远不会改变,因为没有沟通可以把东西放在那里。

  5. 如果您的计算是迭代的,则必须在每次迭代之前执行光环。 如果与计算相比,晕轮交换太慢,您可以增加晕轮的厚度并使其厚两层或多层。 使用外层更新内层中的值。 三层厚的晕轮需要每三次迭代交换一次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM