为啥使用大于每个多处理器内核数的线程块

Posted

技术标签:

【中文标题】为啥使用大于每个多处理器内核数的线程块【英文标题】:Why use thread blocks larger than the number of cores per multiprocessor为什么使用大于每个多处理器内核数的线程块 【发布时间】:2019-12-01 12:18:05 【问题描述】:

我有一块 Nvidia GeForce GTX 960M 显卡,其规格如下:

多处理器:5 个 每个多处理器的内核数:128(即 5 x 128 = 总共 640 个内核) 每个多处理器的最大线程数:2048 最大块大小(x、y、z):(1024、1024、64) 翘曲尺寸:32

如果我运行 1 个 640 个线程块,那么单个多处理器将获得 640 个线程的工作负载,但一次只能并发运行 128 个线程。但是,如果我运行 5 个 128 个线程的块,那么每个多处理器都会获得一个块,并且所有 640 个线程都同时运行。因此,只要我创建 128 个线程的块,那么每个多处理器的线程分布就可以尽可能均匀(假设总共至少 640 个线程)。

然后我的问题是:我为什么要创建大小大于每个多处理器内核数的块(只要我没有达到每个维度的最大块数)?

【问题讨论】:

【参考方案1】:

如果我运行 1 个 640 个线程块,那么单个多处理器将获得 640 个线程的工作负载,但一次只能并发运行 128 个线程。

这是不正确的。所有 640 个线程同时运行。 SM 具有指令延迟并且是流水线的,因此所有线程都处于活动状态并同时具有状态。线程不绑定到特定的内核,执行模型与传统的多线程 CPU 执行模型有很大不同。

但是,如果我运行 5 个 128 个线程的块,那么每个多处理器都会获得一个块,并且所有 640 个线程都同时运行。

这可能会发生,但不能保证。所有块都将运行。它们运行在什么 SM 上由块调度机制决定,这些启发式方法没有记录。

所以,只要我创建了 128 个线程的块,那么每个多处理器的线程分布就可以尽可能均匀(假设总共至少有 640 个线程)。

从上面的答案来看,这也不符合。

然后我的问题是:我为什么要创建大小大于每个多处理器内核数的块(只要我没有达到每个维度的最大块数)?

由于线程与内核无关,因此该架构存在大量延迟,并且需要大量运行中的线程来隐藏所有延迟并达到最佳性能。不幸的是,基本上您在问题中假设的这些论点都不正确或与确定给定设备的最佳块数或其大小相关。

【讨论】:

为什么?占用与问题完全无关。它与执行模型无关。它(充其量)是一个粗略的性能指标。 到目前为止,至少是一种单向相关性,低占用率恰好阻碍了隐藏延迟的能力。但不可否认,我在这里知道的主要事情是我知道的不够多。当你说入住率不相关时,我会删除评论。 我建议你查看 Vasily Volkov 的作品。特别是他通过探索指令级并行性展示了一个简单的 GEMM 内核在 12% 的占用率下以大约 90% 的峰值触发器运行的部分。它充其量只是一个非常粗略的指标。

以上是关于为啥使用大于每个多处理器内核数的线程块的主要内容,如果未能解决你的问题,请参考以下文章

多线程 - 多线程基础

CUDA - 多处理器、Warp 大小和每个块的最大线程数:确切的关系是啥?

以编程方式检索每个多处理器的最大块数

为啥允许我运行块数超过 GPU 的 CUDA 核心数的 CUDA 内核?

线程块网格和多处理器

Python 多处理 - 为啥每个进程有这么多线程?