MPI邻居减少操作

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个相邻进程)

这一刻我觉得我需要像MPI\u Neighbor\u allreduce这样的东西,但我知道

前言

给定一个3D MPI笛卡尔拓扑,描述3D物理域如何分布在进程之间,我编写了一个函数
probe
,该函数要求给定域内一个点的3个坐标的标量值(应该放在一个简单的
REAL::val

只有
1
2
4
8
过程实际涉及
val
的计算

  • 1
    如果点是进程子域的内部(且不涉及邻居)
  • 2
    如果点位于两个进程子域之间的面上(每个进程都有一个邻居参与)
  • 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\u Neighbor\u allgather
    在相邻进程之间共享
    found
    ,使每个相关进程知道
    6个
    邻居中的哪一个参与了总和,然后执行单个
    MPI\u发送
    (s)和
    MPI\u recv
    (s)进行通信
    val
    。但这仍然涉及到每个进程(即使每个进程只与
    6
    邻居通信)
  • 也许最好的选择是每个进程定义一个由自身加上
    6
    邻居组成的通信器,然后使用它
  • 编辑

    对于@JorgeBellón提到的死锁风险,我最初通过在
    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个通讯器

    • 将特定过程放置在模具中心的通信器
    • 每个相邻过程的相应通信器
    这可能是一个相当昂贵的过程,但这并不意味着它值得(例如,HPLinpack广泛使用派生的通讯器)

    如果您已经有了笛卡尔拓扑,一个好的方法是使用
    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