【发布时间】:2019-06-18 17:29:02
【问题描述】:
我正在尝试在我拥有的一些 Fortran 代码上实现 OpenACC。该代码由一个外部时间步进循环(不能并行化)组成,循环内有许多嵌套循环。这些嵌套循环可以并行化,但需要按顺序运行(即 A 后 B 后 C)。
我想将整个过程卸载到 GPU 上,因为在 GPU 和 CPU 之间跨越多个时间步进行数据传输会成为一种禁止性的惩罚。下面的伪代码说明了我目前的方法:
!$acc data copy(ALL THE DATA THAT I NEED)
DO iter = 1, NT
value = array_of_values(iter)
!$ acc kernels
!PART A
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmaxput
!$acc loop independent, private(l)
DO L=0,zmax
if(value == 0) then
(DO SOME COMPUTATIONS...)
elseif(value < 0) then
(DO SOME OTHER COMPUTATIONS...)
elseif(value > 0) then
(DO SOME OTHER COMPUTATIONS...)
endif
ENDDO
ENDDO
ENDDO
!NOW GO DO OTHER STUFF
!PART B
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmax
!$acc loop independent, private(l)
DO L=0,zmax
(DO SOME EVEN MORE COMPUTATIONS...)
ENDDO
ENDDO
ENDDO
!PART C
!etc...
!$acc end kernels
ENDDO
!$acc end data
我有使用这种方法的工作代码,但是,当我使用 NVIDIA 的 Visual Profiler (click for image) 在 GeForce MX150 GPU 上对其进行分析时,我发现通过时间步进循环的每次迭代都会导致没有计算的大时间间隙正在做。 Driver API 表示在此期间它正在执行“cuLaunchKernel”。如果我简单地复制整个循环,以便每个时间步运行两次迭代而不是一次,那么时间步循环内不存在这种间隙,只有在循环开始时才存在。
我有几个(相互关联的)问题:
1.有没有办法让这些内核在其他内核运行时启动?
2. 我读过here 和here WDDM 驱动程序批处理内核启动,这似乎发生在这里。这是否意味着如果我要在 Linux 上运行,我不应该期待这种行为?
cuStreamSynchronize 似乎也阻止了 GPU 运行,从而导致额外的空时间。这似乎与如何在时间步进循环结束之前启动其他内核的问题有关。
这是我第一次使用 OpenACC。我已经到处寻找答案,但可能使用了错误的关键字,因为我找不到任何东西。
编辑 - 解决方案
根据 Mat 的建议,我添加了 Async 来解决问题。有趣的是,内核启动仍然在同一时间完成,但现在每个将在迭代时间步进循环时启动的内核都在程序开始时立即启动。更新后的伪代码以及其他一些对其他人有帮助的调整如下:
!$acc data copy(ALL THE DATA THAT I NEED)
!$acc wait
DO iter = 1, NT
value = array_of_values(iter)
!$acc update device(value, iter), async(1) !allows loop to run on cpu and sends value to GPU
!PART A
!$acc kernels, async(1)
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmaxput
!$acc loop independent, private(l)
DO L=0,zmax
if(value == 0) then
(DO SOME COMPUTATIONS...)
elseif(value < 0) then
(DO SOME OTHER COMPUTATIONS...)
elseif(value > 0) then
(DO SOME OTHER COMPUTATIONS...)
endif
ENDDO
ENDDO
ENDDO
!$acc end kernels
!NOW GO DO OTHER STUFF
!PART B
!$acc kernels, async(1)
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmax
!$acc loop independent, private(l)
DO L=0,zmax
(DO SOME EVEN MORE COMPUTATIONS...)
ENDDO
ENDDO
ENDDO
!$acc end kernels
!PART C
!$acc kernels, async(1)
!for loops, etc...
!$acc end kernels
ENDDO
!$acc wait
!$acc end data
【问题讨论】: