如何为OpenMP改写含SAVE和ENTRY语句的Fortran代码?
改写含SAVE-ENTRY结构Fortran-77子程序的可行方案(适配OpenMP)
先分析你现有模块方案的潜在问题
你当前的模块实现和原F77代码存在一个关键逻辑差异:
原代码中K = 33.0是在主子程序SUMM的执行流程中完成的,只有首次调用SUMM时才会执行该赋值;而你用DATA语句静态初始化K=33.0,如果原程序的初始化逻辑包含动态判断、依赖外部输入等动态逻辑,这种静态初始化就会偏离原逻辑,导致功能异常。此外,OpenMP环境下若未明确变量的共享/私有属性,线程间的状态冲突也会引发问题。
可行改写方案
方案1:修复现有模块,模拟原初始化逻辑
保留模块结构,添加初始化控制逻辑,确保和原代码的首次调用初始化行为一致,同时适配OpenMP:
MODULE SUMM IMPLICIT NONE REAL B(2,2), K LOGICAL :: initialized = .FALSE. ! 标记是否已完成初始化 DATA B/1.0,-3.7,4.3,0.0/ !$OMP THREADPRIVATE(B, K, initialized) ! 按需选择:线程私有/共享 CONTAINS ! 模拟原主子程序SUMM的初始化逻辑 SUBROUTINE init_summ() IF (.NOT. initialized) THEN K = 33.0 initialized = .TRUE. ENDIF END SUBROUTINE init_summ SUBROUTINE sum2(i1,j1) INTEGER i1,j1 CALL init_summ() ! 确保初始化完成 i1 = i1 + j1 WRITE (6,*) 'The first element of B is', B(1,1), '.' WRITE (6,*) 'The K is', K, '.' WRITE (6,*) 'The sum of the numbers is', i1, '.' END SUBROUTINE sum2 SUBROUTINE sum1(i1) INTEGER i1 CALL init_summ() ! 确保初始化完成 WRITE (6,*) 'The first element of B is', B(1,1), '.' WRITE (6,*) 'The K is', K, '.' WRITE (6,*) 'The sum of the numbers is', i1, '.' END SUBROUTINE sum1 END MODULE SUMM
- 若需要所有线程共享同一状态,去掉
!$OMP THREADPRIVATE指令,改用临界区保护变量访问; - 若需要每个线程独立维护状态,保留
THREADPRIVATE即可。
方案2:保留ENTRY结构,直接适配OpenMP
如果希望最小化代码改动,可直接在原F77代码基础上添加OpenMP指令,多数现代编译器支持带ENTRY的子程序适配OpenMP:
SUBROUTINE SUMM IMPLICIT REAL*8 (A-H,O-Z) REAL B(2,2), K DATA B/1.0,-3.7,4.3,0.0/ SAVE !$OMP THREADPRIVATE(B, K) ! 按需选择线程私有/共享 K = 33.0 RETURN ENTRY sum2(i,j) !$OMP CRITICAL ! 若变量为共享,需用临界区保护访问 i = i + j WRITE (6,*) 'The first element of B is', B(1,1), '.' WRITE (6,*) 'The K is', K, '.' WRITE (6,*) 'The sum of the numbers is', i, '.' !$OMP END CRITICAL RETURN ENTRY sum1(i) !$OMP CRITICAL WRITE (6,*) 'The first element of B is', B(1,1), '.' WRITE (6,*) 'The K is', K, '.' WRITE (6,*) 'The sum of the numbers is', i, '.' !$OMP END CRITICAL RETURN END
这种方式适合大型程序的渐进式改造,无需重构整体结构。
方案3:用派生类型封装状态(现代化Fortran风格)
用派生类型封装所有原SAVE变量,通过类型绑定过程实现原ENTRY的功能,更清晰且便于OpenMP控制:
MODULE SUMM_TYPE IMPLICIT NONE TYPE :: SummState REAL :: B(2,2) REAL :: K LOGICAL :: initialized = .FALSE. END TYPE SummState ! 全局实例,模拟原SAVE的单例状态 TYPE(SummState) :: summ_inst !$OMP THREADPRIVATE(summ_inst) ! 线程私有实例 CONTAINS SUBROUTINE init_summ(this) TYPE(SummState), INTENT(INOUT) :: this IF (.NOT. this%initialized) THEN this%B = RESHAPE([1.0,-3.7,4.3,0.0], [2,2]) this%K = 33.0 this%initialized = .TRUE. ENDIF END SUBROUTINE init_summ SUBROUTINE sum2(this, i1, j1) TYPE(SummState), INTENT(INOUT) :: this INTEGER, INTENT(INOUT) :: i1, j1 CALL init_summ(this) i1 = i1 + j1 WRITE (6,*) 'The first element of B is', this%B(1,1), '.' WRITE (6,*) 'The K is', this%K, '.' WRITE (6,*) 'The sum of the numbers is', i1, '.' END SUBROUTINE sum2 SUBROUTINE sum1(this, i1) TYPE(SummState), INTENT(INOUT) :: this INTEGER, INTENT(IN) :: i1 CALL init_summ(this) WRITE (6,*) 'The first element of B is', this%B(1,1), '.' WRITE (6,*) 'The K is', this%K, '.' WRITE (6,*) 'The sum of the numbers is', i1, '.' END SUBROUTINE sum1 END MODULE SUMM_TYPE ! 主程序调用示例 PROGRAM sum0 USE SUMM_TYPE INTEGER i,j WRITE (6,*) 'Enter two numbers: ' READ (5,*) i,j IF (i .EQ. 0) THEN CALL sum1(summ_inst, j) ELSE IF (j .EQ. 0) THEN CALL sum1(summ_inst, i) ELSE CALL sum2(summ_inst, i,j) ENDIF END PROGRAM sum0
这种方式状态封装更清晰,便于扩展和调试,OpenMP下可灵活选择线程私有或共享实例。
内容的提问来源于stack exchange,提问作者DJNZ
相关产品推荐
相关产品推荐

