使用嵌套的 OpenACC 例程传递参考参数

Reference Argument Passing with Nested OpenACC Routines

我正在尝试使用 OpenACC 并行化某些 Fortran 90 代码,其中并行化循环调用顺序例程。当我尝试使用 PGI Fortran 编译器 (2020.4) 运行 代码时,我收到一条错误消息,指出传递引用参数会阻止并行化。

我的理解是,这可能是因为一个例程存在于主机上,而另一个例程存在于设备上,但我不清楚我可能在哪里遗漏了导致这种结果的编译指示。

调用例程的基本结构为:

subroutine OuterRoutine(F,G,X,Y) 

      real(wp), dimension(:,:), intent(IN) :: X      
      real(wp), dimension(:,:), intent(IN) :: Y
      real(wp), dimension(1,PT), intent(OUT) :: F
      real(wp), dimension(N_p,PT), intent(OUT) :: G
        
      ! Local Variables
      integer :: t, i, j

      !$acc data copyin(X,Y), copyout(F,G)

      !$acc parallel loop
      do t = 1,PT,1
            
          !$acc loop collapse(2) reduction(+:intr)
          do i = 1,N_int-1,1
            do j = 1,N_int-1,1
              G(i,j) = intgrdJ2(X(i,j),X(j,i),Y(i,j),Y(j,i),t)
            end do
          end do
          !$acc end loop

      !$acc end parallel loop

      !$acc end data

end subroutine OuterRoutine 

被调用的函数是:

function intgrdJ2(z,mu,p,q,t)
    !$acc routine seq
    
    real(wp), intent(IN) :: z, mu, p, q
    integer, intent(IN) :: t
    real(wp) :: intgrdJ2
        
    ! Local Variables
    real(wp) :: mu2
    real(wp), dimension(N_p) :: nu_m2, psi_m2
    integer :: i
        
    mu2 = (mu*fh_pdf(z,mu,p))/f_pdf(z,mu,p)
        
    do i = 1,N_p,1
        nu_m2(i) = interpValue(mu2,mugrid,nu_knots(:,i,t))
        psi_m2(i) = interpValue(mu2,mugrid,psi_knots(:,i,t))
    end do
    
    intgrdJ2 = nu_m2(i)*psi_m2(i)
    
end function intgrdJ2

例程interpValuefh_pdff_pdf都包含在一个用过的模块中,记为!$acc routine seq。变量mugridnu_knotspsi_knots都是module-级别变量,在调用 OuterRoutine.

之前复制到设备中

当我 运行 代码时,我从编译器得到这样的输出:

intgrdj2:
    576, Generating acc routine seq
         Generating Tesla code
    593, Reference argument passing prevents parallelization: mu2

其中 593 指的是“nu_m2(i) = ...”行。

我的理解是,由于变量 mu2 是在顺序例程中声明的标量,每个线程都应该有自己的变量副本,我不需要在我声明数据区域时明确声明它是私有的。从阅读 this post 看来,问题可能与例程所在的位置有关(主机与设备)。但是,似乎所有相关部分都应该在设备上,因为我指定例程是顺序的。

作为第一次使用 OpenACC 的用户,如果能对我可能忽略的内容进行任何解释,我们将不胜感激!

My understanding is that since the variable mu2 is a scalar declared inside of the sequential routine, each thread should have it's own copy of the variable, and I don't need to explicitly declare it to be private when I declare the data region

大多数情况下都是如此。但这里可能发生的情况是,由于默认情况下 Fortran 通过引用传递变量,因此编译器必须假设它的引用可以由模块变量获取。不太可能,但有可能。

解决此问题的典型方法是按值传递标量,即将“值”属性添加到“interpValue”中的参数声明。或者,您可以通过在“i”循环上添加“!$acc loop seq private(mu2)”来显式私有化“mu2”。

现在该消息可能只是表明编译器无法 auto-parallelize 此循环。但由于它是在顺序例程中,所以这无关紧要,您可以安全地忽略该消息。不过,我没有完整的上下文,所以不能 100% 确定这一点。