1

我有一个更大的 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
4

0 回答 0