在 CUDA 中启动许多小内核有多糟糕



我有一个矩形网格。这些矩形中的每一个都由一个矩形点网格组成。矩形内的所有点都可以通过内核中完全相同的指令序列进行处理。我将能够启动一个需要处理 10000 个点的内核,其中每个线程将处理大约 10-50 个点。然而,矩形边缘和角落上的点将导致大量不同的指令序列。

从设计的角度来看,为具有相同指令序列的每组点启动内核会更容易。这意味着某些内核启动只会处理很少的点,可能不到 10 个。

所以我可能会有 4 个内核启动,需要处理 10000 个点(每个线程

10-50 个点),也许有 30-100 个内核启动,每个内核只有几个点(通常每个线程 1 个点)。

我完全

不知道这是否可以接受,或者是否会完全破坏我的表现。如果您能给我一个粗略的估计或至少一些提示,我会很高兴,要考虑什么才能获得估计。

这里有两个因素,我称之为启动开销和执行开销

启动

开销:启动内核的开销为 ~10us(即 0.01ms)。它可能少一点,也可能多一点,这将取决于您的整体系统以及有问题的内核。此值假定您不是作为图形卡在 Windows 上运行(即没有 WDDM)。

如果在启动前有一个大型非阻塞 GPU 调用,则可以完全隐藏此启动开销。一种思考方式是,您有一个准备在 GPU 上执行的任务队列,您可以在执行某些内容时添加到该队列中。启动开销是添加到队列的成本。只要队列中有东西,您就不会看到启动开销使 GPU 匮乏。

执行

开销:一旦内核到达此队列的前面,它就会被执行。这里也有很小的开销。我希望这是~3-4us,尽管同样,您的里程可能会有所不同。这与初始化和从全局内存中移动数据以使内核运行相关联。它还包括停机成本。

可以使用流来减少此执行开销。如果将小内核放在一个单独的流中,并让它们并发执行,则此执行开销可能会被 GPU 上的其他计算隐藏。您不会让整个 GPU 等待一个小问题通过它,相反,只有少量资源将在等待,而 GPU 的其余部分将继续解决您的主要问题。

也许这应该是一个扩展的评论而不是答案,但我希望它能给你一些方向。

启动

许多小内核而不是大内核的性能限制是由于内核启动开销造成的。这个答案应该解释一些关于它的信息,并且还链接了有趣的资源。

但是还有其他方法可以执行任务。假设您的系统(RAM)内存上有那么大的矩形网格,则必须以某种方式将其传输到GPU内存中。这提供了使用内核传输重叠方法(即异步传输)隐藏小内存传输时间的机会。仅当内核需要足够的时间来完成矩形的计算时,此方法才可能有效。

如果所有网格同时适合 GPU 主内存,则可以从一个内核启动多个内核。在这里,您可以找到有关该主题(动态并行性)的更多信息,这是另一个关于该方法减慢速度的有趣问题。这种方法可能不会产生任何性能提升,因为启动这些内核也需要一些时间,但它是你的建议的替代方案,并保持了简单性,隐藏了主代码的一些复杂性。

作为一般建议,更喜欢很少的大数据传输而不是大量较小的数据传输,因为同样适用于内核,以最小化开销。

最新更新