MPI邻居减少操作
这一刻我觉得我需要像MPI\u Neighbor\u allreduce这样的东西,但我知道 前言 给定一个3D MPI笛卡尔拓扑,描述3D物理域如何分布在进程之间,我编写了一个函数MPI邻居减少操作,mpi,nearest-neighbor,topology,cartesian-coordinates,Mpi,Nearest Neighbor,Topology,Cartesian Coordinates,这一刻我觉得我需要像MPI\u Neighbor\u allreduce这样的东西,但我知道 前言 给定一个3D MPI笛卡尔拓扑,描述3D物理域如何分布在进程之间,我编写了一个函数probe,该函数要求给定域内一个点的3个坐标的标量值(应该放在一个简单的REAL::val) 只有1、2、4或8过程实际涉及val的计算 1如果点是进程子域的内部(且不涉及邻居) 2如果点位于两个进程子域之间的面上(每个进程都有一个邻居参与) 4如果该点位于4个进程子域之间的一侧(并且每个进程都有2个相邻进程)
probe
,该函数要求给定域内一个点的3个坐标的标量值(应该放在一个简单的REAL::val
)
只有1
、2
、4
或8
过程实际涉及val
的计算
如果点是进程子域的内部(且不涉及邻居)1
如果点位于两个进程子域之间的面上(每个进程都有一个邻居参与)2
如果该点位于4个进程子域之间的一侧(并且每个进程都有2个相邻进程)4
如果该点是8个进程子域之间的一个顶点(每个进程都有3个邻居)8
probe
之后,每个进程都会保持val
,这对于涉及的进程来说是一个值,0
或NaN
(我通过(de)注释适当的行来决定)对于不涉及的进程来说是一个值。每个进程都知道它是否参与(通过LOGICAL::found
变量),但不知道它是否是唯一参与的进程,如果不是,也不知道谁是参与的邻居
对于1
涉及的流程,该流程的唯一值就足够了,该流程可以编写、使用它或任何需要的内容。
在后三种情况下,必须计算所涉及过程的不同标量值之和(并除以邻域数+1
,即自包含)
问题
实现这种通信和计算的最佳策略是什么
我在考虑什么解决方案
我在考虑以下可能性
probe
之前执行val=0
,然后可以使用MPI(ALL)REDUCE
(通常参与val/=0
的相关进程,所有参与val==0
的其他进程),但这意味着如果要求val
的点数更多,这些点将被连续处理,即使每个点所涉及的过程集不与其他点集重叠MPI\u Neighbor\u allgather
在相邻进程之间共享found
,使每个相关进程知道6个邻居中的哪一个参与了总和,然后执行单个MPI\u发送(s)和MPI\u recv
(s)进行通信val
。但这仍然涉及到每个进程(即使每个进程只与6
邻居通信)
6
邻居组成的通信器,然后使用它MPI_RECV
之前调用MPI_SEND
来解决它,用于正向通信,即那些与谁参与
中的偶数索引对应的通信,反之亦然。作为一种特殊情况,这是不可能的处理一个周期性方向,其中只有两个进程(因为这两个进程中的每一个都会将另一个进程视为正方向和负方向的邻居,从而导致两个进程以相同的顺序调用MPI\u SEND
和MPI\u RECV
,从而导致死锁);这个特殊情况的解决方案是对涉及的人进行以下特别编辑
(我在代码中称之为found\u neigh
):
作为读者的参考,到目前为止我实现的解决方案(一个我不太满意的解决方案)如下所示
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\u Neighbor\u allreduce
不存在。
您可以创建仅包含相邻进程的派生通讯器,然后对它们执行常规的MPI\u Allreduce
。每个进程在3D网格中最多可以有7个通讯器
- 将特定过程放置在模具中心的通信器
- 每个相邻过程的相应通信器
MPI\u Neighbor\u allgather
。这样,您不仅可以知道涉及多少个邻居,还可以知道它是谁
int found; // logical: either 1 or 0
int num_neighbors; // how many neighbors i got
int who_is_involved[num_neighbors]; // unknown, to be received
MPI_Neighbor_allgather( &found, ..., who_is_involved, ..., comm );
int actually_involved = 0;
int r = 0;
MPI_Request reqs[2*num_neighbors];
for( int i = 0; i < num_neighbors; i++ ) {
if( who_is_involved[i] != 0 ) {
actually_involved++;
MPI_Isend( &val, ..., reqs[r++]);
MPI_Irecv( &val, ..., reqs[r++]);
}
}
MPI_Waitall( r, reqs, MPI_STATUSES_IGNORE );
我不清楚如何实践第一个解决方案(答案中的前两个项目).关于第二种方法,我已经定义了MPI笛卡尔拓扑;要指出的是,对于涉及的每个过程,并非所有6个周围的过程都涉及。请重新阅读问题,并在需要时帮助我改进。到目前为止,我一直依赖于我想到的解决方案,但我想我会在您的上一个bu中尝试这个解决方案let point.我会尽快让你知道,只要我有时间尝试一下。我误解了你的问题。我更新了答案。你编写的代码块基本上是我在第2点中假设的解决方案的第一部分。(到目前为止,我实际上已经采纳了).事实上,我使用您的
参与人
作为基础,在参与共享(和求和,等等)的流程中执行个人MPI\u发送
/MPI\u接收
)
int found; // logical: either 1 or 0
int num_neighbors; // how many neighbors i got
int who_is_involved[num_neighbors]; // unknown, to be received
MPI_Neighbor_allgather( &found, ..., who_is_involved, ..., comm );
int actually_involved = 0;
int r = 0;
MPI_Request reqs[2*num_neighbors];
for( int i = 0; i < num_neighbors; i++ ) {
if( who_is_involved[i] != 0 ) {
actually_involved++;
MPI_Isend( &val, ..., reqs[r++]);
MPI_Irecv( &val, ..., reqs[r++]);
}
}
MPI_Waitall( r, reqs, MPI_STATUSES_IGNORE );
INTEGER :: neighbor, num_neighbors, found
REAL :: val
REAL :: sendbuf(2)
REAL :: recvbuf(2,num_neighbors)
sendbuf(1) = found
sendbuf(2) = val
CALL MPI_Neighbor_allgather( sendbuf, 1, MPI_2REAL, recvbuf, num_neighbors, MPI_2REAL, ...)
DO neighbor = 1,num_neighbors
IF recvbuf(1,neighbor) .EQ. 1 THEN
! use neighbor val, placed in recvbuf(2,neighbor)
END IF
END DO