是否可以对给定代码的 Cuda 编程中使用的内核数量设置限制?

Posted

技术标签:

【中文标题】是否可以对给定代码的 Cuda 编程中使用的内核数量设置限制?【英文标题】:Is it possible to set a limit on the number of cores to be used in Cuda programming for a given code? 【发布时间】:2015-03-15 16:01:22 【问题描述】:

假设我有 Nvidia K40,出于某种原因,我希望我的代码只使用部分 Cuda 内核(例如,而不是使用所有 2880 只使用 400 个内核),这可能吗?这样做是否合乎逻辑任何一个? 此外,当我运行我的代码时,有什么方法可以查看 GPU 正在使用多少个内核?换句话说,我们是否可以在执行期间检查代码正在使用多少个内核,报告喜欢 Windows 中的“任务管理器”或 Linux 中的顶部?

【问题讨论】:

不,这几乎不可能;这也没有意义。无论如何,你为什么要做这样的事情?最接近使用部分内核的方法是通过不启动足够的线程来故意未充分利用设备。不用说,这与大多数人想要做的完全相反。 据我所知,执行内核的 SM 数量受您启动的线程块数量的限制。在此评论中,线程块不会从 SM 迁移到 SM。 这并不常见,但我很确定 cuda 保留切换分配给线程块的 SM 的权利。我认为如果一个 SM 很早就完成并且另一个 SM 负载很高,就会发生这种情况。 我认为您无法控制线程块和 SM 之间的映射。但最起码,你可以知道哪个SM执行哪个threadblock。 @void_ptr,你可以有几个独立的内核。我看到 30 个 SM 和 30 个块的内核完全占用 GPU。确实,在 30 个 SM 处执行 30 个块相对于 30/4=8 个 SM 执行 30 个块要快一些。但是,如果您以这种方式运行一个内核,则第二个内核将等待完成。但是用 1 块加载 SM 是无效的,所以也这样运行。但是 GPU 不知道我们是否要在这个具有 30 个块的内核之后运行另一个内核 - 将它传播到所有 SM 上是否有效。所以一些手动的方式说这应该存在 【参考方案1】:

这是可能的,但这个概念在某种程度上违背了 cuda 的基本最佳实践。并不是说它对某事没有用处。例如,如果您想在同一个 GPU 上运行多个内核,并且出于某种原因想要为每个内核分配一定数量的 Streaming Multiprocessors。也许这对于没有完美内存访问模式的内核的 L1 缓存可能是有益的(我仍然认为对于 99% 的情况,手动共享内存方法会更好)。

如何做到这一点,就是访问 ptx 标识符 %nsmid 和 %smid 并以内核的原始启动为条件。每个流式多处理器 (SM) 必须只有 1 个块,然后根据您想要哪个 SM 上的哪个内核返回每个内核。

我会警告说,这种方法应该保留给非常有经验的 cuda 程序员,并且只能作为性能的最后手段。此外,正如我在评论中提到的,我记得读过一个线程块可以从一个 SM 迁移到另一个,因此必须在实施之前测量行为,并且可能取决于硬件和 cuda 版本。然而,既然你问了,而且我确实相信这是可能的(尽管不推荐),这里有一些资源可以完成你提到的事情。

用于 SM 索引和 SM 数量的 PTS 寄存器... http://docs.nvidia.com/cuda/parallel-thread-execution/#identifiers

以及如何在不直接编写ptx的情况下在cuda内核中使用它... https://gist.github.com/allanmac/4751080

【讨论】:

【参考方案2】:

不确定它是否适用于 K40,但对于较新的 Ampere GPU,可以使用 MIG 多实例 GPU 功能对 GPU 进行分区。

https://docs.nvidia.com/datacenter/tesla/mig-user-guide/

【讨论】:

【参考方案3】:

我不知道这些方法,但想了解一下。

至于问题 2,我想有时这很有用。当您有复杂的执行图、许多内核(其中一些可以并行执行)时,您希望以最有效的方式完全加载 GPU。但似乎在它自己的 GPU 上可以用一个内核的单个块占用所有 SM。 IE。如果你有一个 30 块网格和 30 个 SM 的内核,这个内核可以占用整个 GPU。我相信我看到了这样的效果。这个内核确实会更快(对于每个 SM 4 256 个线程块,可能是 1.5 倍),但是当您有其他工作时,这将无效。

GPU 不知道我们是否要在这个具有 30 个块的内核之后运行另一个内核 - 将它传播到所有 SM 上是否会更有效。因此,应该存在一些手动方式来说明这一点

至于问题 3,我认为 GPU 分析工具应该会显示这一点,Visual Profiler 和更新的 Parallel Nsight 和 Nsight Compute。但我没有尝试。这不是任务管理器,而是由您的程序执行的内核的统计信息。

关于必要时在 SM 之间移动线程块的可能性, @ChristianSarofeen,我找不到提到这是可能的。相当的国家,

每个 CUDA 块由一个流式多处理器 (SM) 执行,并且 不能迁移到 GPU 中的其他 SM(抢占期间除外, 调试,或 CUDA 动态并行)。 https://developer.nvidia.com/blog/cuda-refresher-cuda-programming-model/

虽然从某些架构开始,但存在诸如抢占之类的东西。我记得 NVidia 通过以下方式宣传它。假设您制作了一个运行一些重内核的游戏(例如用于图形渲染)。然后发生了一些不寻常的事情。您需要尽快执行一些不太重的内核。通过抢占,您可以以某种方式卸载正在运行的内核并执行这个高优先级的内核。这会大大增加(这个高公关内核的)执行时间。

我也发现了这样的东西:

CUDA Graphs 展示了一种在 CUDA 中提交工作的新模型。图表 是一系列操作,例如内核启动,由 依赖关系,它与它的执行分开定义。这 允许定义一次图形,然后重复启动。 将图的定义与其执行分开可以实现 优化数量:首先,CPU启动成本相比降低 流,因为大部分设置都是提前完成的;第二, 将整个工作流程呈现给 CUDA 可以实现优化 分段工作提交机制可能无法实现 流。 https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cuda-graphs

我不相信内核调用会花费很多时间(当然,在内核流的情况下,如果您不等待结果)。如果您调用多个内核,则似乎可以在第一个内核在 GPU 上执行时为所有内核发送所有必要的数据。所以我相信 NVidia 意味着它并行运行多个内核并在 SM 之间执行一些智能负载平衡。

【讨论】:

下面的文章看起来很有趣:onlinelibrary.wiley.com/doi/10.1002/cpe.4409“通过重新排序并发内核提交来最大化 GPU 资源使用率”。有点奇怪的方法,但报告速度提高了大约 2 倍 还有这个:dl.acm.org/doi/pdf/10.1145/…“揭秘 NVIDIA GPU 并发内核线程块调度程序的放置策略”他们声明每个 SM 可以同时处理来自不同内核的线程块。这减少了使用单个块加载所有 SM 的问题,但仍然保留了非最优调度的位置

以上是关于是否可以对给定代码的 Cuda 编程中使用的内核数量设置限制?的主要内容,如果未能解决你的问题,请参考以下文章

你能以编程方式知道 GPU 中每个块的最大块数和线程数吗?

CUDA 内核中的中值选择

单个 CUDA 内核可以启动的最大线程数

CUDA 计算和复制引擎队列限制

CUDA 内核代码的设备内存:它是不是明确可管理?

简单cuda内核添加:2432内核调用后内存非法