【问题标题】:Reducing time to launch kernels in time stepping loop - OpenACC减少在时间步进循环中启动内核的时间 - OpenACC
【发布时间】: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. 我读过herehere 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

【问题讨论】:

    标签: cuda fortran gpu openacc


    【解决方案1】:

    当您说“大时间间隔”时,您能更具体一点吗?你需要几秒,微秒,毫秒吗?虽然变化很大,但我预计内核启动开销约为 40 微秒。启动开销通常会在噪音中丢失,但如果内核特别快或者内核启动数百万次,那么它会影响相对性能。使用“异步”子句有助于隐藏启动开销(见下文)。

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

    1. 有没有办法让这些内核在其他内核运行时启动?

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

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

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

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

    【讨论】:

    • 间隔大约是 100 微秒,大约是每个时间步的 20%,考虑到有数千个时间步,它加起来很快。使用 Async 解决了这个问题。谢谢!
    【解决方案2】:

    您可以通过让您的 GPU 在没有显示器的情况下运行来估计 WDDM 的不同之处。如果没有显示器连接到它,它消除了使用 WDDM 运行的一些问题。

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

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

    【讨论】:

    • 如果没有连接显示器,GPU 不会自动从 WDDM 切换到 TCC 模式。使用 nvidia-smi 将模式从 WDDM 更改为 TCC,前提是您的 GPU 支持(Tesla 卡支持)。
    • @tera 这不是特斯拉,它是 GeForce 卡。
    • 在这种情况下,不幸的是,TCC 不是一个选项。但是切换到 Linux 是。
    • 我发布的解决方案为我消除了类似的问题,所以也许它也可以在这里工作。
    • 连接显示器可能会在 GPU 忙于绘制屏幕内容时引入额外的周期性延迟,当然可以通过断开显示器来避免这些延迟。但驱动程序仍将保持在 WDDM 模式并招致相关惩罚,这可以通过切换到 TCC 模式或 Linux 来避免。
    猜你喜欢
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 2013-08-10
    • 2018-04-12
    • 2013-08-29
    • 1970-01-01
    相关资源
    最近更新 更多