如何在Vulkan中跨多个计算队列执行并行计算着色器?

7
更新:此问题已得到解决,您可以在此处找到更多详细信息:https://dev59.com/8b3pa4cB1Zd3GeqPkb0R#64405505 之前也有人提出了类似的问题,但那个问题最初侧重于使用多个命令缓冲区,在不同的线程上触发提交以实现着色器的并行执行。大多数答案建议使用多个队列来解决问题。使用多个队列似乎也是各种博客文章和Khronos论坛回答中的共识。我尝试了那些建议,在多个队列上运行着色器执行,但没有看到并行执行效果,所以我想问一下我可能做错了什么。正如建议的那样,这个问题包含了多个计算着色器的可运行代码被提交到多个队列,并希望对其他正在寻求实现相同功能的人有用。
当前实现位于此拉取请求/分支中,但我将涵盖主要的Vulkan特定点,以确保只需要Vulkan知识来回答这个问题。还值得一提的是,当前用例特别针对计算队列和计算着色器,而不是图形或传输队列(尽管实现跨这些队列实现并行性的见解/经验仍将非常有用,并且很可能也会导致答案)。
更具体地说,我有以下内容:
- 首先获取多个队列——我的设备是NVIDIA 1650,支持在队列族索引0中有16个图形+计算队列,以及在队列族索引2中有8个计算队列。 - evalAsync执行提交(其中包含记录的着色器命令)——您应该注意到创建了一个能够使用的障碍。此外,提交没有任何waitStageMasks(PipelineStageFlags)。 - evalAwait允许我们等待障碍——在调用evalAwait时,我们能够通过创建的几何图形来等待提交完成。
还有一些在上面的示例中看不到但很重要的点:
  • 所有的evalAsync都在同一个应用程序、实例和设备上运行。
  • 每个evalAsync都使用自己独立的commandBuffer和缓冲区,在单独的队列中执行。
  • 如果你想知道内存屏障是否有关系,我们已经尝试通过完全删除所有memoryBarriers (例如在shader执行之前运行的这个),但是这对性能没有产生任何影响。

在基准测试中使用的测试可以在此处找到,不过需要理解的关键要点如下:

运行测试时,我们首先在同一队列上运行一组“同步”shader执行(数量可变,但我们已经测试了6-16个,后者是最大队列数)。然后我们以异步方式运行它们,当所有evalAsync都完成时,我们才运行evalAwait。当比较两种方法得到的时间时,它们花费相同的时间,即使它们在不同的计算队列中运行。

我的问题是:

  • 我在获取队列时是否有遗漏?
  • Vulkan设置中是否有进一步的参数需要配置以确保异步执行?
  • 可能有任何限制吗,比如操作系统进程只能以同步方式向GPU提交工作负载?
  • 在处理多个队列提交时,是否需要使用多线程才能使并行执行正常运行?

此外,我在网上找到了许多有用的资源,涉及Reddit帖子和Khronos Group论坛,提供了非常详细的概念和理论概述,但我没有找到端到端代码示例显示着色器的并行执行。如果您有任何实际示例可以分享,并具有功能并行执行着色器,则会非常有帮助。

如果有进一步的细节或问题可帮助提供更多背景,请告知,我很乐意回答这些问题并/或提供更多细节。

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

  • Vulkan SDK 1.2
  • Windows 10
  • NVIDIA 1650

其他相关链接:

2个回答

3
您正在获得“异步执行”。您只是没有预料到它的行为方式。
在CPU上,如果您有一个活动线程,则您正在使用一个CPU核心(或超线程)。所有该核心的执行和计算能力都只属于您的线程(忽略抢占)。但与此同时,如果有其他核心,您的一个线程不能使用这些核心的任何计算资源。除非您创建另一个线程。
GPU不是这样工作的。队列不像CPU线程那样。它并不特别涉及特定数量的计算资源。队列仅是通过其执行命令的接口;底层硬件决定如何将命令分配给GPU提供的各种计算资源。
通常发生的情况是,当您执行命令时,硬件尝试使用您的命令完全饱和可用的着色器执行单元。如果有更多的着色器单元可用,而您操作所需的调用数少于该数量,则可以立即为下一个命令提供一些资源。但如果没有,那么整个GPU的计算资源将专门用于执行第一个操作;第二个操作必须等待资源可用后才能开始。
无论您将多少工作项推送到计算队列中,它们都会尽可能地使用尽可能多的计算资源。因此,它们将在某些特定顺序中执行。
队列优先级系统存在,但这些主要有助于确定命令的执行顺序。也就是说,如果高优先级队列有一些需要执行的命令,则它们将在下一次计算资源可用进行新命令时占据优先权。
因此,在3个单独的队列上提交3个分派批处理不会比在包含3个分派操作的单个队列上提交1个批处理更快地完成。
多个队列(相同族的)存在的主要原因是能够从多个线程提交工作项而无需进行线程间同步(以及提供一些可能的提交优先级)。

非常感谢您抽出时间阅读和回答我的问题@Nicol Bolas。最终我成功解决了这个问题,我发现我的NVIDIA显卡在不同的队列族中提交工作负载时支持并发处理-更具体地说,仅能支持一个图形队列和一个计算队列族之间的一次并发命令提交。我在此处添加了更详细的解决方案介绍:https://kompute.cc/overview/async-parallel.html。 - axsauze

3

我已经通过这个建议解决了问题。为了提供更多上下文信息,我试图将命令提交给同一family内的多个队列,但在链接的建议中指出,NVIDIA(和其他GPU厂商)在并行处理命令提交时具有不同的能力范围。

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接