【问题标题】:How to execute parallel compute shaders across multiple compute queues in Vulkan?如何在 Vulkan 中跨多个计算队列执行并行计算着色器?
【发布时间】:2021-01-30 17:13:36
【问题描述】:

更新:已解决,您可以在此处找到更多详细信息:https://stackoverflow.com/a/64405505/1889253

similar question was asked previously,但这个问题最初集中在使用多个命令缓冲区,并触发跨不同线程的提交以实现着色器的并行执行。大多数答案表明解决方案是使用多个队列。使用多个队列似乎也是各种博客文章和 Khronos 论坛答案的共识。我已经尝试了那些跨多个队列运行着色器执行但无法看到并行执行的建议,所以我想问一下我可能做错了什么。正如建议的那样,这个问题包括多个计算着色器的可运行代码被提交到多个队列,希望这对希望做同样事情的其他人有用(一旦解决)。

当前的实现是in this pull request / branch,但是我将介绍主要的 Vulkan 特定点,以确保回答这个问题只需要 Vulkan 知识。还值得一提的是,当前的用例专门用于计算队列和计算着色器,而不是图形或传输队列(尽管在这些方面实现并行性的见解/经验仍然非常有用,并且很可能也会导致答案)。

更具体地说,我有以下几点:

在上面的示例中看不到但很重要的几点:

  • 所有 evalAsync 都在同一个应用程序、实例和设备上运行
  • 每个 evalAsync 都使用自己单独的 commandBuffer 和缓冲区在单独的队列中执行
  • 如果您想知道内存屏障是否有问题,我们已尝试完全移除所有内存屏障(this on for example 在着色器执行之前运行),但这对性能没有任何影响

在基准can be found here 中使用的测试,但是唯一需要理解的关键是:

在运行测试时,我们首先在同一个队列上运行一组“同步”着色器执行(数量是可变的,但我们使用 6-16 进行了测试,后者是队列的最大数量)。然后我们以异步方式运行它们,我们运行所有它们和 evalAwait 直到它们完成。在比较两种方法的结果时间时,即使它们在不同的计算队列中运行,它们所花费的时间也是相同的。

我的问题是:

  • 我目前在获取队列时是否遗漏了什么?
  • vulkan 设置中是否还有其他参数需要配置以确保异步执行?
  • 关于潜在的操作系统进程只能以同步方式向 GPU 提交 GPU 工作负载,是否有任何我可能不知道的限制?
  • 在处理多个队列提交时,是否需要多线程才能使并行执行正常工作?

此外,我在各种 reddit 帖子和 Khronos Group 论坛中找到了一些有用的资源,这些资源提供了关于该主题的非常深入的概念和理论概述,但我没有遇到显示并行执行的端到端代码示例着色器。如果您可以分享任何实用的示例,这些示例具有着色器的并行执行功能,那将非常有帮助。

如果有更多细节或问题可以帮助提供更多背景信息,请告诉我,我们很乐意回答和/或提供更多细节。

为了完整起见,我的测试使用的是:

  • Vulkan SDK 1.2
  • Windows 10
  • NVIDIA 1650

已在类似帖子中分享的其他相关链接:

【问题讨论】:

    标签: c++ gpu gpgpu vulkan compute-shader


    【解决方案1】:

    您正在获得“异步执行”。您只是不希望它的行为方式如此。

    在 CPU 上,如果您有一个线程处于活动状态,那么您使用的是一个 CPU 内核(或超线程)。该核心的所有执行和计算能力都单独提供给您的线程(忽略抢占)。但同时,如果还有其他核心,您的一个线程将无法使用这些核心的任何计算资源。除非你创建另一个线程。

    GPU 不是这样工作的。队列类似于 CPU 线程。它并不具体涉及特定数量的计算资源。队列只是执行命令的接口;底层硬件决定如何将命令分配给 GPU 提供的各种计算资源作为一个整体。

    当您执行命令时,通常会发生硬件尝试使用您的命令完全饱和可用的着色器执行单元。如果可用的着色器单元多于操作所需的调用次数,则某些资源可立即用于下一个命令。但如果不是,那么整个 GPU 的计算资源将专用于执行第一个操作;第二个必须等待资源可用才能启动。

    你推入多少个计算队列并不重要;他们都将尝试使用尽可能多的计算资源。因此它们将在很大程度上以某种特定的顺序执行。

    存在队列优先级系统,但这些系统主要帮助确定命令的执行顺序。也就是说,如果高优先级队列有一些命令需要执行,那么下次计算资源可用于新命令时,它们将优先。

    因此,在 3 个单独的队列上提交 3 个调度批次不会比在一个包含 3 个调度操作的队列上提交 1 个批次更快。

    存在多个队列(同一系列)的主要原因是能够从多个线程提交工作,而无需它们进行线程间同步(并提供一些可能的提交优先级)。

    【讨论】:

    • 非常感谢您花时间阅读并回答我的问题@Nicol Bolas - 最后我能够解决它,我发现我的 NVIDIA 卡在工作负载时支持并发处理在不同的 queueFamilies 中提交 - 更具体地说,它只能支持跨一个 Graphics 队列和一个计算族队列的一个并发命令提交。我在这里添加了更详细的解决方案:kompute.cc/overview/async-parallel.html
    【解决方案2】:

    我已经能够使用this suggestion 解决。为了提供进一步的上下文,我试图将命令提交到同一家族中的多个队列,但是在链接的建议中指出,NVIDIA(和其他 GPU 供应商)在并行处理命令时具有不同范围的功能提交。

    在我的特定情况下,我正在测试的 NVIDIA 1650 卡仅支持在不同 queueFamilies 中提交工作负载时的并发处理 - 更具体地说,它只能支持跨一个图形队列和一个计算系列的一个并发命令提交排队。

    我重新实现了代码以允许为特定命令分配族队列,并且我能够实现并行处理(通过跨两个 queueFamilies 提交速度提高了 2 倍)。

    这里有更多关于实现的细节https://kompute.cc/overview/async-parallel.html

    【讨论】:

      猜你喜欢
      • 2018-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-20
      • 2011-04-24
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      相关资源
      最近更新 更多