假设我有一个 Fortran 程序,它在一个数组上执行两个任务:任务 A 计算它的平均值,任务 B 将它加倍。关键是任务 B 应该独立于任务 A。当使用 OpenACC 加速程序时,通过使任务 A 异步来同时运行两个任务是有意义的:
program test
implicit none
integer, parameter :: n = 1000000
real(8) :: mean
real(8) :: array(n)
real(8) :: array_d(n)
! initialize array
array = [(i, i=1, n)]
!$acc kernels async num_gangs(1)
! Task A: get mean of array
mean = 0d0
!$acc loop independent reduction(+:mean)
do i = 1, n
mean = mean + array(i)
end do
mean = mean / n
!$acc end kernels
!$acc kernels
! Task B: work on array
!$acc loop independent
do i = 1, n
array(i) = array(i) * 2
end do
!$acc end kernels
!$acc wait
!$acc end data
! print array and mean
print "(10(g0.2, x))", array(:10)
print "('mean = ', g0.2)", mean
end program
但是,同时运行两个任务时,任务B会修改任务A正在读取的数组,导致值不正确。在 CPU 上(无加速)我得到:
2.0 4.0 6.0 8.0 10. 12. 14. 16. 18. 20.
mean = 500000.5000000000
在 GPU(使用 NVIDIA HPC SDK)上,我得到一个不同的平均值,这显然是不正确的:
2.0 4.0 6.0 8.0 10. 12. 14. 16. 18. 20.
mean = 999967.6836640000
是否有一种优雅的方式来“保护”任务 A 正在工作的阵列?