减少在时间步进循环中启动内核的时间-OpenACC

时间:2019-01-24 21:46:30

标签: cuda fortran gpu openacc

我试图在我拥有的某些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上对其进行配置时,我发现整个时间步进循环中的每次迭代都会导致较大的时间间隔,而没有计算正在完成。驱动程序API在这段时间内表示正在执行“ cuLaunchKernel”。如果我只是复制整个循环,以便每个时间步运行两次迭代,而不是每次迭代,那么只有在循环开始时,时间步进循环中才不存在此间隙。

我有几个(相关的)问题:
1。。有没有办法让其他内核正在运行时启动这些内核?
2。。我已经读过herehere,其中WDDM驱动程序批处理了内核启动,这似乎正在此处进行。这是否意味着如果我要在Linux上运行,就不要期望这种行为?

cuStreamSynchronize似乎也阻止了GPU的运行,从而导致了额外的空时间。这似乎与如何在时间步进循环结束之前启动其他内核的问题有关。

这是我第一次使用OpenACC。我到处都在寻找答案,但由于我找不到任何东西,可能使用了错误的关键字。

编辑-解决方案

根据Mat 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

2 个答案:

答案 0 :(得分:1)

当您说“较大的时间间隔”时,您能说得更具体些吗?您是要秒,微秒还是毫秒?尽管变化很大,但我预计内核启动开销约为40微秒。通常,启动开销会因噪音而丢失,但是,如果内核特别快或启动了数百万次,则可能会影响相对性能。使用“异步”子句可以帮助隐藏启动开销(请参见下文)。

尽管如果差距更大,那么可能还会有其他事情发生。例如,如果循环减少,则可以将减少变量复制回主机。请注意,如果您使用的是PGI,请查看编译器反馈消息(-Minfo = accel)。这可能会为正在发生的事情提供一些线索。

  
      
  1. 是否有办法在其他内核正在运行时启动这些内核?
  2.   

是的。使用三个单独的“内核”区域,每个部分一个。然后在每个计算区域中添加“ async(1)”子句。异步将在启动内核之后使主机继续运行,并且由于它们使用相同的队列,在这种情况下为1,但是您可以使用任何正整数,它将创建一个依赖项,因此B不会运行直到A完成,而C会在B之后启动。您将要在其中希望主机与设备同步的地方添加“!$ acc等待”。

请注意,异步队列映射到CUDA流。

  

cuStreamSynchronize似乎也阻止了GPU的运行,   导致额外的空时间。这似乎与问题有关   如何在时间结束之前启动其他内核   步进循环。

这是阻止主机等待GPU计算完成的时间。它应该与您的内核运行时间大致相同(如果不使用异步)。

答案 1 :(得分:0)

通过让GPU在不显示的情况下运行,您可以估算WDDM的不同之处。如果未连接任何显示器,则可以消除使用WDDM运行的一些问题。

如果显示器支持同时支持集成显卡和GPU的驱动程序,请尝试将显示器连接至主板(检查BIOS)。

否则,您可以在PC中添加另一个GPU(也许是一个旧的GPU)并将其用于显示器。