【发布时间】:2018-01-30 15:44:20
【问题描述】:
此时我觉得我需要MPI_Neighbor_allreduce 这样的东西,但我知道it doesn't exist。
前言
给定一个描述 3D 物理域如何在进程之间分布的 3D MPI 笛卡尔拓扑,我编写了一个函数 probe,它要求一个标量值(应该放在一个简单的 REAL :: val 中)给定 3域内点的坐标。
只有1、2、4 或8 进程实际参与val 的计算。
-
1如果该点是内部进程子域(并且它没有涉及邻居), -
2如果该点位于 2 个进程的子域之间的面上(并且每个子域都涉及 1 个邻居), -
4如果该点在 4 个进程的子域之间在一边(并且每个子域都涉及 2 个邻居), -
8如果该点是 8 个进程的子域之间的一个顶点(并且每个进程都涉及 3 个邻居)。
在像现在一样调用probe 之后,每个进程都持有val,这对于所涉及的进程0 或NaN 来说是一些价值(我通过(de)评论适当的行来决定)不涉及的过程。每个进程都知道它是否参与(通过LOGICAL :: found 变量),但不知道它是否是唯一参与的进程,如果不是,也不知道谁是参与的邻居。
在1涉及的进程的情况下,该唯一进程的唯一值就足够了,并且该进程可以编写它,使用它或任何需要的东西。
在后三种情况下,必须计算所涉及进程的不同标量值的总和(并除以邻居的数量+1,即自包含)。
问题
完成这种通信和计算的最佳策略是什么?
我在考虑什么解决方案
我正在考虑以下可能性。
- 每个进程在调用
probe之前执行val = 0,然后可以使用MPI_(ALL)REDUCE,(所涉及的进程通常以val /= 0参与,所有其他进程以val == 0参与),但这意味着如果要求val获得更多点,则这些点将被连续处理,即使每个点所涉及的过程集不与其他集重叠。 - 每个进程调用
MPI_Neighbor_allgather以在相邻进程之间共享found,以使每个参与的进程知道6邻居中的哪一个参与总和,然后执行单独的MPI_send(s ) 和MPI_recv(s) 与val通信。但这仍将涉及每个进程(即使每个进程仅与6邻居通信。 - 也许最好的选择是每个进程定义一个由自身加上
6邻居组成的通信器,然后使用。
编辑
对于@JorgeBellón 提到的关于死锁风险的问题,我最初通过在MPI_RECV 之前调用MPI_SEND 进行正向通信来解决它,即那些对应于@ 中偶数索引的通信987654357@,反之亦然。作为一种特殊情况,这不能处理只有两个进程的周期性方向(因为两个进程中的每一个都会将另一个进程视为正负方向的邻居,因此导致两个进程都调用MPI_SEND 和MPI_RECV 顺序相同,从而导致死锁);这种特殊情况的解决方案是对who_is_involved(我在我的代码中称为found_neigh)进行以下ad-hoc编辑:
DO id = 1, ndims
IF (ALL(found_neigh(2*id - 1:2*id))) found_neigh(2*id -1 + mycoords(id)) = .FALSE.
END DO
作为读者参考,我目前实现的解决方案(我不太满意的解决方案)如下。
found = ... ! .TRUE. or .FALSE. depending whether the process is/isn't involved in computation of val
IF ( found) val = ... ! compute own contribution
IF (.NOT. found) val = NaN
! share found among neighbors
found_neigh(:) = .FALSE.
CALL MPI_NEIGHBOR_ALLGATHER(found, 1, MPI_LOGICAL, found_neigh, 1, MPI_LOGICAL, procs_grid, ierr)
found_neigh = found_neigh .AND. found
! modify found_neigh to deal with special case of TWO processes along PERIODIC direction
DO id = 1, ndims
IF (ALL(found_neigh(2*id - 1:2*id))) found_neigh(2*id -1 + mycoords(id)) = .FALSE.
END DO
! exchange contribution with neighbors
val_neigh(:) = NaN
IF (found) THEN
DO id = 1, ndims
IF (found_neigh(2*id)) THEN
CALL MPI_SEND(val, 1, MPI_DOUBLE_PRECISION, idp(id), 999, MPI_COMM_WORLD, ierr)
CALL MPI_RECV(val_neigh(2*id), 1, MPI_DOUBLE_PRECISION, idp(id), 666, MPI_COMM_WORLD, MPI_STATUS_IGNORE, ierr)
END IF
IF (found_neigh(2*id - 1)) THEN
CALL MPI_RECV(val_neigh(2*id - 1), 1, MPI_DOUBLE_PRECISION, idm(id), 999, MPI_COMM_WORLD, MPI_STATUS_IGNORE, ierr)
CALL MPI_SEND(val, 1, MPI_DOUBLE_PRECISION, idm(id), 666, MPI_COMM_WORLD, ierr)
END IF
END DO
END IF
! combine own contribution with others
val = somefunc(val, val_neigh)
【问题讨论】:
标签: mpi nearest-neighbor topology cartesian-coordinates