问题描述
更新:此问题已解决,您可以在此处找到更多详细信息:https://stackoverflow.com/a/64405505/1889253
一个similar question was asked previously,但最初的问题集中在使用多个命令缓冲区,并在不同线程之间触发提交以实现着色器的并行执行。大多数答案表明解决方案是改为使用多个队列。在多个博客帖子和Khronos论坛答案中,使用多个队列似乎也是共识。我尝试了那些建议在多个队列中运行着色器执行的建议,但无法看到并行执行,因此我想问一下我可能做错了什么。如建议的那样,该问题包括将多个计算着色器的可运行代码提交给多个队列,希望对希望这样做的其他人(一旦解决)有用。
当前的实现是in this pull request / branch,但是我将介绍Vulkan的主要要点,以确保只需要Vulkan知识即可回答此问题。还值得一提的是,当前用例专用于计算队列和计算着色器,而不是图形或传输队列(尽管获得洞察/经验的经验仍然非常有用,并且很可能会得出答案)。
- Multiple queues first are "fetched"-我的设备是NVIDIA 1650,并且在队列家族索引0中支持16个图形+计算队列,在队列家族索引2中支持8个计算队列
- evalAsync performs the submission (which contains recorded shader commands)-您应该注意到已经创建了我们可以使用的围栏。另外,提交没有任何waitStageMasks(PipelinestageFlags)。
- evalAwait allows us to wait for the fence-调用evalAwait时,我们可以等待通过创建的篱笆完成提交
在以上示例中不可见但很重要的几点:
- 所有evalAsync在相同的应用程序,实例和设备上运行
- 每个evalAsync都使用其自己的单独的commandBuffer和缓冲区,并在单独的队列中执行
- 如果您想知道是否可能需要做一些内存障碍,我们尝试通过完全删除所有memoryBarriers(在着色器执行之前运行的this on for example)来解决问题,但这对性能没有任何影响
基准测试can be found here中使用的测试,但是唯一需要了解的关键是:
-
如您所见,
- This is the shader用于测试,我们只添加了一系列atomicAdd步骤以增加处理时间
- 当前测试的大小为small buffer和high number of shader loop iterations,但是我们也使用较大的缓冲区大小(即100,000而不是10)进行了测试,并且迭代次数较小(1,000而不是100,000,000)。
在运行测试时,我们首先在同一队列上运行一组“同步”着色器执行(数量是可变的,但我们已经使用6-16进行了测试,后者是最大队列数)。然后,我们以异步方式运行它们,在其中运行所有它们和evalAwait,直到它们完成为止。比较两种方法的结果时间时,即使它们跨不同的计算队列运行,它们也花费相同的时间。
我的问题是:
- 获取队列时我当前是否缺少某些东西?
- vulkan设置中是否还需要配置其他参数以确保异步执行?
- 对于仅能够以同步方式向GPU提交GPU工作负载的潜在操作系统进程,我是否可能没有任何限制?
- 在处理多个队列提交时,是否需要多线程才能使并行执行正常工作?
此外,我还在各种reddit帖子和Khronos Group论坛上找到了一些有用的资源,这些资源提供了关于该主题的非常深入的概念和理论概述,但是我还没有遇到能够并行执行的端到端代码示例着色器。如果您可以分享一些实用的示例,并且可以并行执行着色器,那将非常有帮助。
如果还有其他细节或问题可以帮助您提供更多的背景信息,请告诉我,我们很乐意回答和/或提供更多细节。
出于完整性考虑,我的测试使用的是:
- Vulkan SDK 1.2
- Windows 10
- NVIDIA 1650
- Similar discussion with suggested link to example but which seems to have disappeared...
- Post on Leveraging asynchronous queues for concurrent execution(不幸的是没有示例代码)
- (相对较早-5年)Post that suggests nvidia cards can't do parallel execution of shaders,但似乎没有一个令人困惑的答案
- Nvidia演示文稿on Vulkan Multithreading with multiple queue execution(因此我在上面的线程问题)
解决方法
您将获得“异步执行”。您只是不希望它的行为方式如此。
在CPU上,如果有一个活动线程,则说明您正在使用一个CPU内核(或超线程)。所有该内核的执行和计算功能都单独分配给您的线程(忽略抢占)。但是同时,如果有其他核心,则您的一个线程无法使用这些核心的任何计算资源。除非您创建另一个线程,否则不会。
GPU不能那样工作。队列不是像CPU线程。它并不具体涉及特定数量的计算资源。队列只是执行命令的接口;底层的硬件决定了如何将命令分配给GPU整体提供的各种计算资源。
执行命令时通常会发生的情况是,硬件会尝试使用命令完全饱和可用的着色器执行单元。如果可用的着色器单元数超过操作所需的调用次数,则某些资源可立即用于下一个命令。但是,如果没有,那么整个GPU的计算资源将专用于执行第一个操作。第二个必须等待资源可用才能开始。
您应该将多少计算队列投入工作;他们都将尝试使用尽可能多的计算资源。因此它们将在很大程度上按特定顺序执行。
存在队列优先级系统,但是这些系统主要有助于确定命令的执行顺序。也就是说,如果高优先级队列中有一些需要执行的命令,那么下次计算资源可用于新命令时,它们将具有优先级。
因此,在3个单独的队列中提交3个调度批处理不会比在一个包含3个调度操作的队列中提交1个批处理快。
存在多个(相同系列的)队列的主要原因是能够从多个线程提交工作,而无需进行线程间同步(并提供一些可能的提交优先级)。
,我已经能够使用this suggestion解决问题。为了提供进一步的上下文,我试图将命令提交到同一系列中的多个队列,但是在链接的建议中指出,NVIDIA(和其他GPU供应商)在并行处理命令时具有不同的功能范围。提交。
在我的特定情况下,我正在测试的NVIDIA 1650卡仅在将工作负载提交到不同队列系列时才支持并发处理-更具体地说,它仅支持在一个图形队列和一个计算系列中进行一次并发命令提交。队列。
我重新实现了代码,以允许为特定命令分配族队列,并且我能够实现并行处理(通过在两个queueFamilies中提交,速度提高了2倍)。
的更多详细信息