mod_phys_lmdz_omp_transfert
路径
LMDZ.COMMON-6.3\LMDZ.COMMON\libf\phy_common\mod_phys_lmdz_omp_transfert.F90
职责
mod_phys_lmdz_omp_transfert 是物理侧 OpenMP 线程内 transfer 模块。它不做跨 MPI rank 通信,而是在同一个 MPI rank 内把 master 线程持有的 rank 级数据广播给各 OpenMP 线程,或在 klon_mpi rank 列和 klon_omp 线程局部列之间 scatter/gather,并提供线程内求和 reduction。
在完整物理并行层中,调用方通常使用 mod_phys_lmdz_transfert_para 的 bcast/scatter/gather/reduce_sum 泛型;该 wrapper 先处理 MPI 层,再调用本模块完成 OMP 层分发或汇集。底层逐过程细节见 phys-transfer-omp-core。
公开接口和过程清单
模块默认 PRIVATE,只公开:
PUBLIC bcast_omp, scatter_omp, gather_omp, reduce_sum_omp, omp_barrier| 泛型接口 | specific procedures | 类型和维度 | 引擎 |
|---|---|---|---|
bcast_omp |
22 | character scalar;integer/real/logical scalar 到 6D | bcast_omp_cgen, bcast_omp_igen, bcast_omp_rgen, bcast_omp_lgen |
scatter_omp |
12 | integer/real/logical rank 列数组到 4D wrapper | scatter_omp_igen, scatter_omp_rgen, scatter_omp_lgen |
gather_omp |
12 | integer/real/logical thread 列数组到 4D wrapper | gather_omp_igen, gather_omp_rgen, gather_omp_lgen |
reduce_sum_omp |
10 | integer/real scalar 到 4D | reduce_sum_omp_igen, reduce_sum_omp_rgen |
omp_barrier |
1 | 无参数同步工具 | 直接 !$OMP BARRIER |
总计:56 个公开泛型 specific procedures、12 个内部引擎子程序、3 个 check_buffer_* helper 和 1 个 omp_barrier。
模块状态
| 符号 | 类型 | 生命周期 | 用途 |
|---|---|---|---|
grow_factor=1.5 |
REAL, PARAMETER |
编译期常量 | 动态 buffer 扩容比例。 |
size_min=1024 |
INTEGER, PARAMETER |
编译期常量 | 最小 buffer 长度。 |
buffer_c |
CHARACTER(LEN=size_min), SAVE |
模块级共享 | character broadcast 固定缓冲。 |
buffer_i, buffer_r, buffer_l |
allocatable SAVE 数组 |
模块级共享 | integer/real/logical transfer 和 reduction 的共享缓冲。 |
size_i, size_r, size_l |
INTEGER, SAVE |
模块级共享 | 当前已分配 buffer 长度。 |
这些 buffer 不是 THREADPRIVATE。它们依赖 OpenMP barrier、master/single 和 critical 区保证所有线程看到一致的共享状态。
动态 buffer 约定
check_buffer_i 和 check_buffer_r 的模式是:
- 所有线程先进入
!$OMP BARRIER。 !$OMP MASTER判断buff_size > size_*时释放旧 buffer。- 新长度取
MAX(size_min, INT(grow_factor*buff_size))。 - 再经过一个
!$OMP BARRIER,确保后续 transfer 引擎看到已分配 buffer。
check_buffer_l 的逻辑相同,但用 !$OMP SINGLE 替代 MASTER。源码注释说明调试中发现即便有前后 barrier,MASTER 分配 logical buffer 后仍可能被其他线程过早使用;SINGLE 行为更可靠。
character broadcast 不调用 check_buffer_*,只使用固定长度 buffer_c。因此 character 路径适合短配置字符串;若调用方传入超出固定 buffer 的字符串,需要单独确认编译器行为和实际调用长度。
数据分布依赖
scatter/gather 引擎在子程序内部使用:
| 来源模块 | 符号 | 作用 |
|---|---|---|
mod_phys_lmdz_omp_data |
klon_omp |
当前 OpenMP 线程负责的物理列数。 |
mod_phys_lmdz_omp_data |
klon_omp_begin |
当前线程在 rank 内物理列段的 1-based 起点。 |
mod_phys_lmdz_mpi_data |
klon_mpi |
当前 MPI rank 持有的物理列数。 |
由此形成固定形状约定:
scatter_omp: VarIn(klon_mpi, dimsize) -> VarOut(klon_omp, dimsize)
gather_omp : VarIn(klon_omp, dimsize) -> VarOut(klon_mpi, dimsize)
dimsize 由 wrapper 把除第一维之外的维度展平得到。第一维必须是物理列维,不能把 level/tracer/time 维放在第一维。
运行流程
bcast_omp
bcast_omp_*gen 采用 master 写、所有线程读的两段 barrier 模式:
- master 线程把
Var写入共享 buffer。 - barrier 后每个线程从 buffer 写回自己的
Var。 - 第二个 barrier 确保所有线程完成读取。
integer/real/logical scalar wrapper 先用长度为 1 的临时数组包装,再进入数组引擎。5D/6D broadcast 只存在于 bcast_omp,是 COMMON transfer 高维配置广播的重要支撑。
scatter_omp
scatter 将 rank 级数组切给各线程:
- master 将
VarIn(1:klon_mpi, :)拷入共享 buffer。 - barrier 后每个线程根据
klon_omp_begin-1+ij读取自己的列段。 - 第二个 barrier 结束本次分发。
这一路径要求 Init_phys_lmdz_omp_data 已经在 OpenMP parallel 区内完成,使每个线程拥有正确的 klon_omp 和 klon_omp_begin。
gather_omp
integer/logical gather 与 scatter 对称:所有线程先把 VarIn(1:klon_omp, :) 写入 buffer 的 rank 级偏移位置,barrier 后 master 拷出到 VarOut(1:klon_mpi, :)。
real gather 是例外:gather_omp_rgen 不用 buffer_r,而是让 master 把 Varout_ptr => VarOut,barrier 后所有线程通过 shared pointer 直接写入 rank 级输出数组。VarOut 因此带 TARGET 属性;Varout_ptr 是 SAVE pointer,源码注释明确它在线程间共享且不是 THREADPRIVATE。
reduce_sum_omp
reduce_sum_omp_igen/rgen 使用共享 buffer 和 !$OMP CRITICAL:
- master 清零
Buff(:)。 - barrier 后各线程进入 critical 区,把本线程
VarIn累加到 buffer。 - barrier 后 master 把 buffer 写入
VarOut。 - 最后一个 barrier 保证输出对所有线程同步。
该实现偏保守,保证互斥累加;代价是 reduction 阶段串行化,性能热点需要结合调用频率判断。
OMP 指令语义
| 指令 | 本模块用途 |
|---|---|
!$OMP BARRIER |
transfer 阶段边界、buffer 分配前后、指针关联后和输出完成后同步。 |
!$OMP MASTER |
由 master 写 bcast/scatter buffer、读 gather buffer、清零/写回 reduce buffer。 |
!$OMP SINGLE |
logical buffer 分配,规避注释中记录的 MASTER 可见性问题。 |
!$OMP CRITICAL |
reduction 累加互斥。 |
非 OpenMP 编译时,指令作为注释被忽略。此时模块仍可编译,但语义等价于单线程路径;不要把非 OMP 构建下的结果误解为线程同步已被验证。
调用关系
| 方向 | 关系 |
|---|---|
| 上游调用 | phys-transfer-para-wrapper 的 bcast/scatter/gather/reduce_sum 在 OMP 层调用本模块;物理 getin、初始化和 physics wrapper 通常通过该上层泛型间接经过。 |
| 支撑数据 | omp-data-transfer 说明 mod_phys_lmdz_omp_data 的线程列分布;本模块只使用其 klon_omp/klon_omp_begin 状态。 |
| MPI 配合 | mpi-data-transfer 处理 rank 间通信;本模块只处理 rank 内线程间搬运。 |
| 高维支持 | parallel-transfer-6d 记录 5D/6D broadcast 和 OMP buffer/race 修复。 |
I/O、配置和副作用
- 本模块不直接读写文件,不调用
getin,也不直接操作 XIOS/history/restart。 - 主要副作用是更新输入输出实参,以及按需分配/释放模块级
buffer_i/buffer_r/buffer_l。 reduce_sum_omp修改共享 buffer 并通过 critical 累加;调用方不能在未同步的外层逻辑中复用同一共享 transfer buffer。gather_omp_rgen的 shared pointer 只在引擎调用期间有效;它依赖 barrier 避免其他线程在 pointer 未关联时写入。
Mars 运行参与度
条件经过:Mars/COMMON 物理侧启用 OpenMP 物理并行时经过。
典型路径是 physics 初始化或物理参数读取通过 mod_phys_lmdz_transfert_para 的泛型进入本模块。calfis_p 的部分动力-物理场搬运有手写循环,不直接调用本模块;这一区别见 calfis-p-parallel-transfer。
复现和排错顺序
- 确认是否在 OpenMP parallel 区内调用;否则
klon_omp/klon_omp_begin可能只是单线程状态。 - 确认第一维是物理列维:scatter 输入首维为
klon_mpi,scatter 输出首维为klon_omp;gather 反向。 - 对 buffer 问题,先查
buff_size、当前size_*和是否经历了check_buffer_*的前后 barrier。 - 对 logical transfer 竞态,优先确认是否使用 当前
check_buffer_lSINGLE 版本。 - 对 real gather 差异,查
VarOut TARGET和Varout_ptr直接写入,而不是查buffer_r。 - 对高维配置广播,确认
bcast_omp_i5/i6、bcast_omp_r5/r6、bcast_omp_l5/l6已进入泛型接口。
待确认
buffer_c固定为 1024 字符,源码未提供 character buffer 动态扩容;当前未见超长配置字符串调用证据。reduce_sum_omp的 critical 串行化是否构成实际性能热点,需要运行剖面确认,源码本身只能说明同步策略。