我是openacc的新手。我正试图用它来加速粒子代码。然而,我不明白为什么在主机上更新数组(下面程序中的eta
)时,它会根据"!$acc update self
"的位置给出不同的结果。这是一个重新产生这个问题的代码:
program approximateFun
use funs
use paras_mod
integer :: Nx
real(dp) :: dx
real(dp), dimension(:), allocatable :: x, eta
real(dp), dimension(5) :: xp, fAtxp
!$acc declare create(Nx)
!$acc declare create(x)
!$acc declare create(eta)
!$acc declare create(dx)
!$acc declare create(fAtxp)
!$acc declare create(xp)
Nx = 16
!$acc update device(Nx)
xp = (/3.9, 4.1, 4.5, 5.0, 5.6/)
!$acc update device(xp)
allocate(x(1 : Nx))
allocate(eta(1 : Nx))
eta = 0.0d0
dx = 2 * pi / (Nx - 1)
!$acc update device(dx)
do i = 1, Nx
x(i) = (i - 1.0d0) * dx
end do
!$acc update device(x)
call calc_etaVec(x, Nx, eta)
!$acc update self(eta) ! gives the correct results
!$acc parallel loop present(dx, xp, eta, fAtxp)
do i = 1, 5
call calcFunAtx(xp(i), dx, eta, fAtxp(i))
end do
!$acc update self (fAtxp)
!!$acc update self(eta) !---> gives wrong result
write(6, *) 'eta', eta
do i = 1, 5
write(6, *) 'xp, fAtxp', xp(i), fAtxp(i)
end do
deallocate(x)
deallocate(eta)
end program approximateFun
上一个程序使用以下模块
MODULE funs
use paras_mod
implicit none
CONTAINS
subroutine calc_etaVec(x, nx, eta)
integer, intent(in) :: nx
real(dp), dimension(:), intent(in) :: x
real(dp), dimension(:), intent(out) :: eta
integer :: i
!$acc parallel loop present(x, eta)
do i = 1, nx
eta(i) = sin(x(i))
end do
end subroutine
subroutine calcFunAtx(xp, dx, eta, fAtx)
real(dp), intent(in) :: xp, dx
real(dp), dimension(:), intent(in) :: eta
real(dp), intent(out) :: fAtx
integer :: idx
!$acc routine seq
idx = 1 + floor(xp / dx)
fAtx = eta(idx)
end subroutine calcFunAtx
END MODULE
和
module paras_mod
implicit none
save
INTEGER, PARAMETER :: dp = selected_real_kind(14,300)
REAL(dp), PARAMETER :: PI=4.0d0*atan(1.0d0)
end module paras_mod
当在call calc_etaVec(x, Nx, eta)
之后直接使用!$acc update self(eta)
时,eta
被正确更新。但是,当在循环之后使用时,只有前五个元素是正确的,而其余的都是零。这背后的原因是什么?
感谢
call calc_etaVec(x, Nx, eta)
之后直接使用!$acc update self(eta)
时的输出为
0.000000000000000 0.4067366430758002
0.7431448254773941 0.9510565162951535 0.9945218953682734
0.8660254037844388 0.5877852522924732 0.2079116908177597
-0.2079116908177591 -0.5877852522924730 -0.8660254037844384
-0.9945218953682732 -0.9510565162951536 -0.7431448254773946
-0.4067366430758009 -2.4492935982947064E-016
这是正确的。然而,当在循环后使用时,输出为
0.000000000000000 0.4067366430758002
0.7431448254773941 0.9510565162951535 0.9945218953682734
0.000000000000000 0.000000000000000 0.000000000000000
0.000000000000000 0.000000000000000 0.000000000000000
0.000000000000000 0.000000000000000 0.000000000000000
0.000000000000000 0.000000000000000
这个问题一直令人困惑,直到我确定它是编译器错误。我已经提交了一份问题报告,TPR#32673,并将其发送给工程部进行审查。
当设置显示数据移动的环境变量NV_ACC_NOTIFY=2时,我发现编译器只复制了40个字节,而正确的是128个字节。然而,如果我删除";eta";从前面的现在句来看,那是正确的。
#ifdef WORKS
!$acc parallel loop present(dx, fAtxp)
#else
!$acc parallel loop present(dx, fAtxp, eta)
#endif
do i = 1, 5
call calcFunAtx(xp(i), dx, eta, fAtxp(i))
end do
此外,只有在declare-create指令中使用可分配程序时才会发生这种情况。如果您切换到使用数据区域,则不会出现问题。也许是我们以前没有看到它的原因(看起来这些bug是从2020年年中开始出现的)。很少在模块之外的任何程序中使用declare指令。