calfis_p 并行 transfer 架构

输入范围

dynphy_lonlat\calfis_p.F: L1-1520(并行物理调用接口)
phy_common\mod_phys_lmdz_para.F90: L1-78(MPI/OMP 分布初始化)
phy_common\mod_phys_lmdz_mpi_data.F90: L1-220(MPI 分布表)
phy_common\mod_phys_lmdz_omp_data.F90: L1-126(OMP 线程列分布)
phy_common\mod_phys_lmdz_transfert_para.F90(MPI+OMP 统一 transfer 封装)
phy_common\physics_distribution_mod.F90(物理分布初始化链)

Mars 运行参与度:条件经过。Mars 并行动力启用物理时(apphys 为真),calfis_p 在 MPI rank 和 OpenMP 线程两层执行完整的 scatter → call_physiq → gather 流程。

例程定位

本页聚焦 calfis_p 中 MPI 与 OpenMP 两层并行的数据搬运机制,以及它与 phy_common transfer 基础设施的关系。场映射和 tendency 回写的逐场细节已由 calfis-p-grid-mappingcalfis-p-physiq-contract 覆盖。

两层并行数据流

calfis_p 的并行数据搬运分为三个层次:

动力网格 (iip1,jjp1,llm)
    ↓ scatter (OMP DO / MASTER, klon=klon_mpi)
rank 级物理列 z*(klon_mpi, llm, ...)
    ↓ OMP scatter (手动拷贝, offset=klon_omp_begin-1)
线程级物理列 *_omp(klon_omp, llm, ...)
    ↓ call_physiq (nsplit_phys 循环)
线程级 tendency *_omp(klon_omp, llm, ...)
    ↓ OMP gather (手动拷贝回)
rank 级 tendency z*(klon_mpi, llm, ...)
    ↓ MPI wind exchange (手写 ISSEND/IRECV)
扩展数组 zdufi2/zdvfi2(klon_mpi+iim, llm)
    ↓ gather (OMP DO / MASTER)
动力网格 tendency pdufi/pdvfi/pdhfi/pdqfi/pdpsfi

编译守卫

calfis_p 的并行实现依赖三层预处理守卫:

守卫 控制范围 Mars 并行动力
CPP_PARA parallel_lmdz(omp_chunk、AllGather_Field、jj_begin/jj_end)、Write_FieldTimescpdet_mod 启用
CPP_PHYS dimphymod_phys_lmdz_mpi_datamod_phys_lmdz_omp_datamod_interface_dyn_physcallphysiq_mod 启用
CPP_MPI mpif.hMPI_StatusMPI_ISSEND/IRECV/WAITALL 启用

如果没有 CPP_PHYS,源码打印 calfis_p: for now can only work with parallel physicsstopCPP_MPI 控制手写 wind exchange 内的 MPI 调用;非 MPI 构建跳过整段交换。

rank 级 scatter:OMP 指令模式

rank 级 scatter 把动力二维数组转为 klon_mpi 物理列,全程使用 klon=klon_mpi。OMP 指令遵循固定模式:

标量场逐层 scatter(zplev、zpk、zteta、zqfi、zplay):

c$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
      DO l = 1, llm
        do ig0 = 1, klon
          i = index_i(ig0);  j = index_j(ig0)
          zpk(ig0,l) = ppk(i,j,l)
        enddo
      ENDO
c$OMP END DO NOWAIT

所有线程共享 index_i/index_j(非 THREADPRIVATE),但写入 z* 数组的不同 ig0 位置,因此不存在竞争。NOWAIT 允许后续独立 scatter 立即启动。

MASTER 独占段(zpsrf、timer、moyzon):

c$OMP MASTER
      do ig0 = 1, klon
        zpsrf(ig0) = pps(index_i(ig0), index_j(ig0))
      enddo
c$OMP END MASTER

zpsrf scatter 放在 MASTER 段是因为后续 OMP DO 隐式 barrier 会等待 master 完成。

BARRIER 同步点:scatter 完成后、进入 OMP 线程级切片前有一个显式 c$OMP BARRIER(L752),确保所有线程看到完整的 rank 级 z* 数组。

OMP 线程级 scatter/gather

first_omp 分配

每个 OpenMP 线程首次进入时(first_omp 标志,THREADPRIVATE),按 klon_omp 分配 *_omp 工作区:

c$OMP BARRIER
      if (first_omp) then
        klon = klon_omp
        allocate(zplev_omp(klon, llm+1))
        allocate(zplay_omp(klon, llm))
        ! ... 共 20 个 allocate ...
        allocate(zdqfic_omp(klon, llm, nqtot))
        allocate(flxwfi_omp(klon, llm))
        first_omp = .false.
      endif

first_ompTHREADPRIVATE,因此每个线程独立判断和分配。分配完成后所有线程的 *_omp 数组维度为 klon_omp(可能不同线程列数不同,余数优先给低编号线程)。

手动 scatter

BARRIER 后,源码把 klon 改为 klon_omp,设置 offset = klon_omp_begin - 1,然后逐数组手动拷贝:

      klon = klon_omp
      offset = klon_omp_begin - 1
      do l = 1, llm+1
        do i = 1, klon
          zplev_omp(i, l) = zplev(offset+i, l)
        enddo
      enddo

被 scatter 到 *_omp 的数组共 14 个:zplev_ompzplay_ompzpk_ompzphi_ompzphis_omppresnivs_ompzufi_ompzvfi_ompzrfi_ompztfi_ompzqfi_ompzdufi_ompzdvfi_ompzdtfi_ompzdqfi_omp

这不是 phy_commonscatter_omp 泛型调用,而是手写循环。原因可能是 calfis_pCPP_PARA+CPP_PHYS 内编译,直接依赖物理列数组布局,而 scatter_omp 泛型接口不一定覆盖所有维度和类型组合。

手动 gather

physics 调用后(包括 nsplit_phys 循环和 tendency 平均),源码把 *_omp 结果拷贝回 rank 级 z*

      do l = 1, llm
        do i = 1, klon
          zufi(offset+i, l) = zufi_omp(i, l)
        enddo
      enddo

gather 回 z* 的数组共 13 个(含 zdpsrf)。之后 klon 恢复为 klon_mpi500 CONTINUE 标签后 BARRIER 同步。

MPI wind exchange

physics 结束后,calfis_pusing_mpi 为真时执行一段手写 MPI 交换,这是 calfis_p 唯一直接使用 MPI 调用的段落:

发送阶段

      IF (using_mpi) THEN
      if (MPI_rank > 0) then
c$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
       DO l = 1, llm
        du_send(1:iim, l) = zdufi(1:iim, l)
        dv_send(1:iim, l) = zdvfi(1:iim, l)
       ENDDO
c$OMP END DO NOWAIT
c$OMP BARRIER
c$OMP MASTER
!$OMP CRITICAL (MPI)
        call MPI_ISSEND(du_send, iim*llm, MPI_REAL8, MPI_Rank-1, 401, ...)
        call MPI_ISSEND(dv_send, iim*llm, MPI_REAL8, MPI_Rank-1, 402, ...)
!$OMP END CRITICAL (MPI)
c$OMP END MASTER
c$OMP BARRIER
      endif

rank > 0 的 rank 把前 iimzdufi/zdvfi 打包发送给 rank-1。打包用 OMP DO 并行化,发送用 MASTER + CRITICAL(MPI) 保护。

接收阶段

      if (MPI_rank < MPI_Size-1) then
c$OMP BARRIER
c$OMP MASTER
!$OMP CRITICAL (MPI)
        call MPI_IRECV(du_recv, iim*llm, MPI_REAL8, MPI_Rank+1, 401, ...)
        call MPI_IRECV(dv_recv, iim*llm, MPI_REAL8, MPI_Rank+1, 402, ...)
!$OMP END CRITICAL (MPI)
c$OMP END MASTER
      endif
c$OMP BARRIER

rank < size-1rank+1 接收。BARRIER 确保所有线程在 WAITALL 前同步。

等待和组装

c$OMP MASTER
!$OMP CRITICAL (MPI)
      if (MPI_rank>0 .and. MPI_rank<MPI_Size-1) then
        call MPI_WAITALL(4, Req(1), Status, ierr)
      else if (MPI_rank>0) then
        call MPI_WAITALL(2, Req(1), Status, ierr)
      else if (MPI_rank<MPI_Size-1) then
        call MPI_WAITALL(2, Req(3), Status, ierr)
      endif
!$OMP END CRITICAL (MPI)
c$OMP END MASTER
c$OMP BARRIER
      ENDIF ! using_mpi

中间 rank 等待 4 个 request(2 发 + 2 收),边界 rank 等待 2 个。

扩展数组构造

c$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
      DO l = 1, llm
        zdufi2(1:klon, l) = zdufi(1:klon, l)
        zdufi2(klon+1:klon+iim, l) = du_recv(1:iim, l)
        zdvfi2(1:klon, l) = zdvfi(1:klon, l)
        zdvfi2(klon+1:klon+iim, l) = dv_recv(1:iim, l)
        ! 同时清零 jj_begin / jj_end tendency ...
      ENDDO
c$OMP END DO NOWAIT

这段同时完成三件事:构造扩展数组、清零边界纬圈 tendency、为后续 gather 做准备。OMP DO 并行化层循环。

与 phy_common transfer 基础设施的关系

直接使用的 phy_common 符号

来源模块 符号 calfis_p 中的用途
mod_phys_lmdz_mpi_data mpi_master(别名 mpi_root_xx 引入但未在主线直接使用
mod_phys_lmdz_mpi_data MPI_rank, MPI_Size, is_north_pole_dyn, is_south_pole_dyn rank/pole 判断
mod_phys_lmdz_omp_data klon_omp, klon_omp_begin 线程列切片
mod_const_mpi COMM_LMDZ MPI wind exchange communicator
parallel_lmdz omp_chunk, using_mpi, AllGather_Field, jj_begin/jj_end OMP chunk、MPI 标志、全局场收集

不直接使用的 phy_common transfer

calfis_p 不调用 mod_phys_lmdz_transfert_parabcast/scatter/gather 泛型,也不调用 mod_phys_lmdz_mpi_transfertmod_phys_lmdz_omp_transfert 的任何过程。原因是:

  1. scatter 路径不同phy_commonscatter_mpi/scatter_ompklon_glo(全局物理列)和 klon_mpi/klon_omp 之间搬运,使用 klon_mpi_para_begin/end 分布表。calfis_p 的 scatter 是从动力 (iip1,jjp1) 二维格点到 klon_mpi 一维列,由 index_i/index_j 驱动,两者映射方式不同。

  2. gather 路径不同phy_commongather_mpiklon_mpi 收集回 klon_glocalfis_p 的 gather 是从 klon_mpi 一维列写回 (iip1,jjp1) 二维动力格点,由 gr_fi_dyn_p 和手动回写完成。

  3. wind exchange 独有:MPI wind exchange 是 calfis_p 特有的跨 rank 通信,服务于 pdvfi 跨纬带 stencil。phy_common 的 MPI transfer 族不包含这类自定义 stencil 交换。

AllGather_Field:Titan 专用

AllGather_Field 来自 parallel_lmdz,不是 phy_common transfer。它只在 Titan 分支(planet_type=="titan")和 flag_moyzon 为真时使用,把整个二维动力场收集到所有 rank,用于计算 zonal/global mean 剖面。Mars 主线不经过这段。

      if (planet_type.eq."titan") then
        call AllGather_Field(pp, iip1*jjp1, llmp1)
        call AllGather_Field(pteta, iip1*jjp1, llm)
        ! ... 计算 plevmoy, tmoy, tetamoy, phimoy 等全局均值 ...
      endif

运行时并行守卫

除了编译守卫,calfis_p 在运行时还有多层条件分支:

守卫 类型 控制范围
using_mpi logical MPI wind exchange 整段(L1182-1241)
is_north_pole_dyn logical scatter 极点 kstart=2、极点 wind 投影、gather 极点行扩展
is_south_pole_dyn logical scatter 极点 kend=klon-1、极点 wind 投影、jj_end 不清零
firstcal SAVE logical 首次调用分配、debut 标志
first_omp THREADPRIVATE logical 每个线程首次 _omp 分配
flag_moyzon logical Titan/Venus zonal mean 分支(moyzon_ch/moyzon_mu)
MPI_rank integer wind exchange 发送/接收边界判断

THREADPRIVATE 清单

calfis_p 声明的 THREADPRIVATE 变量分为两组:

OMP 工作区(20 个数组 + 1 个标志):

c$OMP THREADPRIVATE(zplev_omp, zplay_omp, zpk_omp, zphi_omp, zphis_omp,
c$OMP+              presnivs_omp, zufi_omp, zvfi_omp, ztfi_omp,
c$OMP+              zrfi_omp, zqfi_omp, zdufi_omp, zdvfi_omp,
c$OMP+              zdtfi_omp, zdqfi_omp, zdpsrf_omp, flxwfi_omp,
c$OMP+              zdufic_omp, zdvfic_omp, zdtfic_omp, zdqfic_omp)

控制标志(3 个):

c$OMP THREADPRIVATE(first_omp)
c$OMP THREADPRIVATE(firstcal, debut)

firstcaldebut 都是 THREADPRIVATE,这意味着每个线程独立维护首次调用状态。首次调用初始化在 OMP MASTER 段完成 rank 级分配,first_omp 控制线程级分配。

SAVE 工作区生命周期

rank 级 z* 数组使用 ALLOCATABLE, SAVE:首次调用时分配,后续调用复用同一内存。线程级 *_omp 数组同理,但每个线程独立分配。

du_send/dv_send/du_recv/dv_recv 是固定维度 (iim, llm) 的 SAVE 数组,不需要分配。zdufi2/zdvfi2ALLOCATABLE, SAVE,维度 klon+iim,首次调用时分配。

与串行 calfis 的并行差异

方面 串行 calfis 并行 calfis_p
全局 (iip1,jjp1) 本 rank (jj_begin:jj_end)
列索引 ngridmx 全局物理列 klon_mpi 本地物理列
OMP 两层:rank 级 OMP DO + 线程级 klon_omp
MPI wind exchange(ISSEND/IRECV)
极点 直接包含在 ngridmx 中 is_north/south_pole_dyn 控制
AllGather Titan 专用
工作区 固定维度 ALLOCATABLE, SAVE
编译守卫 无 CPP_PARA/CPP_PHYS CPP_PARA + CPP_PHYS + CPP_MPI

复现要点

相关页面

待确认