嗨,关于多个内核的Cuda流处理的几个问题。假设在具有3.5能力的kepler设备中有s个流和一个内核,其中s<=32内核使用大小为n的dev_input数组和大小为s*n的dev输出数组。内核从输入数组中读取数据,将其值存储在寄存器中,对其进行操作,并将结果写回位于s*n+tid位置的dev_output。
我们的目标是每次使用n个流中的一个来运行相同的内核s次。类似于simpleHyperQ示例。你能评论一下以下任何一项是否以及如何影响并发性吗?
- dev_input和dev_output未固定
- dev_input现状与dev_input大小s*n,其中每个内核读取唯一的数据(没有读取冲突)
- 内核从常量内存读取数据
- 每个块分配10kb的共享内存
- 内核使用60个寄存器
任何好的评论都将不胜感激。。。!!!
欢呼,Thanasio
Robert,非常感谢你详细的回答。这很有帮助。我编辑了4,它是10kb每块。因此,在我的情况下,我启动了61个块和256个线程的网格。核在计算上是有界的。我启动了同一内核的8个流。对它们进行分析,然后我看到前两者之间有很好的重叠,然后情况越来越糟。内核执行时间约为6ms。前两个流几乎完全同时执行后,其余流之间的距离为3ms。关于5,我使用了一个K20,它有一个255寄存器堆。所以我不认为会有缺点。我真的不明白为什么我没有实现与gk110所指定的相同的并发性。。
请查看以下链接。有一个名为kF.png的图像。它显示流的探查器输出。。!!!
https://devtalk.nvidia.com/default/topic/531740/cuda-programming-and-performance/concurrent-streams-and-hyperq-for-k20/
内核之间的并发性取决于许多因素,但许多人忽略的一个因素只是内核的大小(即网格中的块数)。具有能够有效利用GPU的大小的内核通常不会在很大程度上同时运行,即使它们同时运行,也几乎没有吞吐量优势。GPU内部的工作分配器通常会在内核一启动就开始分发块,因此,如果一个内核在另一个内核之前启动,并且两者都有大量块,那么第一个内核通常会占用GPU,直到它接近完成,在这一点上,第二个内核的块将被调度和执行,也许有少量的"并发重叠"。
要点是,具有足够块来"填满GPU"的内核将阻止其他内核实际执行,除了调度之外,这在计算3.5设备上没有任何不同。此外,除了为整个内核指定几个参数外,在块级别指定启动参数和统计信息(如寄存器使用情况、共享内存使用情况等)也有助于提供清晰的答案。compute 3.5体系结构在这一领域的好处仍然主要来自"少数"块的"小"内核,试图一起执行。Compute 3.5在这方面有一些优势。
你还应该复习一下这个问题的答案。
- 当内核使用的全局内存没有固定时,它会影响数据传输的速度,以及重叠复制和计算的能力,但不会影响两个内核同时执行的能力。然而,对复制和计算重叠的限制可能会扭曲应用程序的行为
- 不应该有"阅读冲突",我不知道你说的是什么意思。允许两个独立的线程/块/网格读取全局内存中的相同位置。一般来说,这将在二级缓存级别进行排序。只要我们谈论只是读,就不应该有冲突,也不应该对并发性产生特别的影响
- 恒定内存是一种有限的资源,在设备上执行的所有内核之间共享(请尝试运行deviceQuery)。如果您还没有超过总设备限制,那么唯一的问题将是常量缓存的利用率,以及缓存抖动之类的问题。除了这种次要关系之外,对并发性没有直接影响
- 识别每个块而不是每个内核的共享内存的数量会更有指导意义。这将直接影响SM上可以调度的块数。但是,如果您指定了每个内核的启动配置以及启动调用的相对时间,那么回答这个问题也会更加清晰如果共享内存恰好是调度中的限制因素,那么您可以将每个SM的可用共享内存总数除以每个内核的使用量,从而在此基础上了解可能的并发性。我自己的观点是,每个网格中的块数可能是一个更大的问题,除非你的内核每个网格使用10k,但整个网格中只有几个块
- 我在这里的评论与我对4的回应几乎相同。查看设备的deviceQuery,如果寄存器成为每个SM上调度块的限制因素,那么您可以将每个SM的可用寄存器除以每个内核的寄存器使用量(同样,谈论每个块的寄存器使用率和内核中的块数更有意义),以发现限制可能是什么
同样,如果你有合理大小的内核(数百或数千个块,或更多),那么工作分配器对块的调度很可能是内核之间并发量的主要因素。
编辑:针对问题中发布的新信息。我看过kF.png
- 首先让我们从每个SM的块的角度进行分析。CC 3.5允许每个SM有16个"打开"或当前计划的块。如果你要启动两个内核,每个内核61个块,那么这可能足以填满CC 3.5设备上的"准备就绪"队列。换句话说,GPU一次可以处理其中的两个内核。当其中一个内核的块"耗尽"时,工作分配器会调度另一个内核。第一个内核的块在大约一半的总时间内充分"耗尽",因此下一个内核在前两个内核完成的一半左右被调度,因此在任何给定点(在时间线上画一条垂直线),都有2个或3个内核同时执行。(根据图表,启动的第三个内核与前两个内核重叠了约50%,我不同意你的说法,即每次连续的内核启动之间有3ms的距离)。如果我们说在峰值时,我们有3个内核被调度(有很多垂直线将与3个内核时间线相交),并且每个内核有大约60个块,那么这大约是180个块。你的K20有13个SM,每个SM上最多可以安排16个区块。这意味着在峰值时,你安排了大约180个区块(可能),而理论峰值为16*13=208。所以你在这里已经非常接近max了,而且你不可能得到更多。但也许你认为你只得到120/208,我不知道
- 现在让我们从共享内存的角度来看一下。一个关键问题是您的L1/共享拆分设置是什么?我相信它默认为每个SM 48KB的共享内存,但如果您更改了此设置,这将非常重要。无论如何,根据您的声明,每个计划块将使用10KB的共享内存。这意味着我们将最大化每个SM调度大约4个块,或者总共4*13个块=在任何给定时间可以调度的最大52个块。你显然超过了这个数字,所以我可能没有足够的关于内核共享内存使用情况的信息。如果你真的使用10kb/block,这或多或少会阻止你一次执行多个内核的线程块。可能还有一些重叠,我相信这可能是您申请的实际限制因素。60个块的第一个内核被调度。在几个块耗尽之后(或者可能是因为两个内核启动得足够近),第二个内核开始被调度,所以几乎是同时进行的。然后,我们必须等待一段时间,等待大约一个内核的块耗尽,然后才能安排第三个内核,这很可能是时间线中指示的50%
无论如何,我认为上面的分析1和2清楚地表明,基于内核结构固有的限制,您正在从设备中获得大部分功能。(我们可以基于寄存器进行类似的分析,以发现这是否是一个重要的限制因素,在大多数情况下,在达到可以同时执行的最大内核数量的限制之前,您将遇到其他类型的机器限制。
编辑:关于文档和资源,我从Greg Smith链接到上面的答案提供了一些资源链接。以下是更多信息:
- C编程指南中有一节介绍异步并发执行
- NVIDIA的Steve Rennich博士的GPU并发和流演示在NVIDIA网络研讨会页面上
到目前为止,我使用HyperQ的经验是内核并行化的2-3(3.5)倍,因为内核通常更大,用于更复杂的计算。小内核是另一回事,但通常内核更复杂。
Nvidia在其cuda 5.0文档中也回答了这一问题,即更复杂的内核将减少并行化的数量。
但是,GK110有一个很大的优势,就是允许这样做。