我有一个更大的 Fortran 程序,我正在尝试转换它,以便计算密集型部分将在使用 OpenMP 和/或 OpenACC 的 NVidia GPU 上运行。在开发过程中,我遇到了一些问题,以了解如何在 GPU 上执行的子例程中使用模块中声明的变量(其中一些也在 CPU 上)。因此,我创建了一个小示例,并通过试验和添加相应的 OpenMP 和 OpenACC 指令来解决这个问题。我在此消息的末尾包含了构成我的示例的三个文件。
正如我认为我已经理解了一些事情并且我的示例程序可以工作一样,我注意到以下内容:
- 我使用 OpenMP 指令使用 gcc 10.2 编译程序:
gfortran -O3 -fopenmp -Wall -Wextra test_link.f90 parameters.f90 common_vars.f90 -o test_link
结果与预期一致,即数组 XMO 的所有元素都是 1,DCP 的所有元素都是 2,IS1 的元素是 3,IS2 的元素是 24。
- 我使用 OpenACC 指令使用 PGI 编译器 19.10 社区版编译程序:
pgfortran -O4 -acc -ta=tesla,cc35 -Minfo=all,mp,accel -Mcuda=cuda10.0 test_link.f90 common_vars.f90 parameters.f90 -o test_link
结果与上述相同。
- 我使用 OpenACC 指令使用 gcc 10.2 编译程序:
gfortran -O3 -fopenacc -Wall -Wextra test_link.f90 parameters.f90 common_vars.f90 -o test_link
数组 XMO、DCP 和 IS1 的结果是正确的,但是 IS2 的所有元素都是 0。很容易验证变量 NR 的值为 0 来得到这个结果。
我的理解是,我的示例的 OpenMP 和 OpenACC 版本是等效的,但我无法弄清楚为什么 OpenACC 版本仅适用于 PGI 编译器而不适用于 gcc。
如果可能,请提供不需要更改代码而只需要更改指令的解决方案。正如我所提到的,我的原始代码要大得多,包含更多的模块变量,并在代码中调用更多的子例程以在 GPU 上执行。更改该代码将变得更加困难,显然我宁愿仅在确实有必要时才这样做。
先感谢您!
我的示例文件如下。
File parameters.f90
MODULE PARAMETERS
IMPLICIT NONE
INTEGER, PARAMETER :: MAX_SOURCE_POSITIONS = 100
END MODULE PARAMETERS
File common_vars.f90
MODULE COMMON_VARS
USE PARAMETERS
IMPLICIT NONE
!$OMP DECLARE TARGET TO(NR)
INTEGER :: NR
!$ACC DECLARE COPYIN(NR)
END MODULE COMMON_VARS
File test_link.f90
SUBROUTINE TEST()
USE COMMON_VARS
IMPLICIT NONE
!$OMP DECLARE TARGET
!$ACC ROUTINE SEQ
INTEGER I
I = NR
END SUBROUTINE TEST
PROGRAM TEST_LINK
USE COMMON_VARS
USE PARAMETERS
IMPLICIT NONE
INTERFACE
SUBROUTINE TEST()
!$OMP DECLARE TARGET
!$ACC ROUTINE SEQ
END SUBROUTINE TEST
END INTERFACE
REAL :: XMO(MAX_SOURCE_POSITIONS), DCP(MAX_SOURCE_POSITIONS)
INTEGER :: IS1(MAX_SOURCE_POSITIONS), IS2(MAX_SOURCE_POSITIONS)
INTEGER :: X, Y, Z, MAX_X, MAX_Y, MAX_Z, ISOUR
MAX_X = 3
MAX_Y = 4
MAX_Z = 5
NR = 6
!$OMP TARGET UPDATE TO(NR)
!$OMP TARGET MAP(TOFROM:IS1,IS2,DCP,XMO)
!$OMP TEAMS DISTRIBUTE PARALLEL DO COLLAPSE(3)
!$ACC UPDATE DEVICE(NR)
!$ACC PARALLEL LOOP GANG WORKER COLLAPSE(3) INDEPENDENT &
!$ACC COPY(IS1,IS2,DCP,XMO)
DO X = 1, MAX_X
DO Y = 1, MAX_Y
DO Z = 1, MAX_Z
ISOUR = (X - 1)*MAX_Y*MAX_Z + (Y - 1)*MAX_Z + Z
XMO(ISOUR) = 1.0
DCP(ISOUR) = 2.0
IS1(ISOUR) = 3
IS2(ISOUR) = 4 * NR
CALL TEST()
ENDDO ! End of z loop
ENDDO ! End of y loop
ENDDO ! End of x loop
!$ACC END PARALLEL LOOP
!$OMP END TEAMS DISTRIBUTE PARALLEL DO
!$OMP END TARGET
DO X = 1, MAX_X
DO Y = 1, MAX_Y
DO Z = 1, MAX_Z
ISOUR = (X - 1)*MAX_Y*MAX_Z + (Y - 1)*MAX_Z + Z
WRITE(*, *) 'ISOUR = ', ISOUR, 'XMO = ', XMO(ISOUR), 'DCP = ', DCP(ISOUR), 'IS1 = ', IS1(ISOUR), 'IS2 = ', IS2(ISOUR)
ENDDO ! End of z loop
ENDDO ! End of y loop
ENDDO ! End of x loop
END PROGRAM TEST_LINK