为啥 Cuda/OpenCL 的全局内存中没有银行冲突?

Posted

技术标签:

【中文标题】为啥 Cuda/OpenCL 的全局内存中没有银行冲突?【英文标题】:Why aren't there bank conflicts in global memory for Cuda/OpenCL?为什么 Cuda/OpenCL 的全局内存中没有银行冲突? 【发布时间】:2011-04-20 01:44:31 【问题描述】:

有一件事我还没有弄清楚,谷歌也没有帮助我,为什么可能与共享内存发生银行冲突,但在全局内存中却没有?会不会与寄存器发生银行冲突?

更新 哇,我真的很感谢 Tibbit 和 Grizzly 的两个答案。看来我只能给一个答案打一个绿色的复选标记。我对堆栈溢出很陌生。我想我必须选择一个答案作为最佳答案。对于我没有给绿色检查的答案,我可以做些什么来感谢您吗?

【问题讨论】:

您可以随时为您喜欢的任何问题或答案投票 Bank 冲突可能发生在内存层次结构的其他级别以及寄存器文件中。共享内存库冲突会显着影响内核性能,并且完全可以由开发人员控制。其他类型的银行冲突对性能的影响较小,开发人员无法解决,因此不会与开发人员沟通。 【参考方案1】:

简短回答:在全局内存或寄存器中都没有存储体冲突。

说明:

理解为什么的关键是掌握操作的粒度。单个线程不访问全局内存。全局内存访问是“合并的”。由于全局内存非常慢,因此块内线程的任何访问都被组合在一起,以尽可能少地向全局内存发出请求。

线程可以同时访问共享内存。当两个线程尝试访问同一个 bank 中的地址时,这会导致 bank 冲突。

寄存器不能被任何线程访问,除了分配给它的线程。由于您无法读取或写入我的寄存器,因此您无法阻止我访问它们——因此,没有任何银行冲突。

谁可以读写全局内存?

Only blocks。单个线程可以进行访问,但事务将在块级别处理(实际上是 warp / half warp 级别,但我尽量不要复杂)。如果两个块访问同一个内存,我不相信它会花费更长的时间,而且它可能会被最新设备中的 L1 缓存加速——尽管这不是很明显。

谁可以读写共享内存?

Any thread within a given block. 如果每个块只有 1 个线程,则不会发生银行冲突,但不会有合理的性能。银行冲突的发生是因为一个块被分配了几个线程,比如 512 个线程,它们都在争夺同一个银行中的不同地址(不完全相同的地址)。在 CUDA C 编程指南的末尾有一些关于这些冲突的精彩图片——图 G2,第 167 页(实际上是 pdf 的第 177 页)。 Link to version 3.2

谁可以读写寄存器?

Only the specific thread to which it is allocated. 因此一次只有一个线程在访问它。

【讨论】:

请注意,我对 L1 缓存的评论实际上是我自己的问题——L1 缓存中是否发生了银行冲突。由于这完全是在硬件中处理的,我不相信我们在最新的文档中被告知过。 (但 L1 仅在最新的 2.* 硬件中——因此,如果您没有 Fermi GPU,则此时它会静音)。【参考方案2】:

在给定类型的内存上是否可能存在存储冲突显然取决于内存的结构,因此也取决于其用途。

那么为什么共享内存的设计方式允许银行冲突呢?

这相对简单,设计一个可以同时处理对同一内存的独立访问的内存控制器并不容易(事实证明大多数人都做不到)。因此,为了允许半扭曲中的每个线程访问一个单独的寻址字,内存被存储起来,每个存储区都有一个独立的控制器(至少人们可以这么想,不确定实际的硬件)。这些银行是交错的,以使顺序线程快速访问顺序内存。因此,这些银行中的每一个都可以一次处理一个请求,理想情况下允许在半扭曲中同时执行所有请求(显然,由于这些银行的独立性,这种模型理论上可以维持更高的带宽,这也是一个优势)。

寄存器呢?

寄存器被设计为作为 ALU 指令的操作数访问,这意味着它们必须以非常低的延迟进行访问。因此,他们获得了更多的晶体管/位来实现这一点。我不确定在现代处理器中如何准确地访问寄存器(不是您经常需要的信息,也不是那么容易找到)。然而,在银行中组织寄存器显然是非常不切实际的(对于更简单的架构,您通常会看到所有寄存器都挂在一个大型多路复用器上)。所以不,寄存器不会发生银行冲突。

全局内存

首先,全局内存在不同的粒度上工作,然后是共享内存。内存以 32、64 或 128 字节的块访问(至少对于 GT200,对于 fermi,它始终是 128B,但缓存,AMD 有点不同),每次你想要从一个块中获取一些东西时,整个块都会被访问/传输。这就是您需要合并访问的原因,因为如果每个线程都从不同的块访问内存,则您必须传输所有块。

但谁说没有银行冲突?我对此并不完全确定,因为我还没有找到任何实际的资源来支持 NVIDIA 硬件,但这似乎是合乎逻辑的: 全局内存通常分布在几个 ram 芯片上(可以通过查看显卡轻松验证)。这是有道理的,如果这些芯片中的每一个都像一个本地内存库,那么如果在同一个库上同时有多个请求,您就会遇到库冲突。然而,对一件事的影响会不那么明显(因为内存访问所消耗的大部分时间是从 A 到 B 获取数据的延迟),并且它不会是一个工作组“内部”的明显影响(因为一次只执行一个 halfwarp 并且如果该 halfwarp 发出多于一个请求,则您有一个未合并的内存访问,因此您已经受到了打击,因此很难衡量这种冲突的影响。所以只有在以下情况下才会发生冲突几个工作组尝试访问同一个银行。在 gpgpu 的典型情况下,您有一个大型数据集位于顺序内存中,因此效果不应该真正明显,因为有足够的其他工作组同时访问其他银行,但它应该可以构建数据集仅以少数银行为中心的情况,这会影响带宽(因为最大带宽将来自对所有银行的平均分配访问权限,因此每个银行都会仅具有该带宽的一小部分)。同样,我还没有读过任何东西来证明这个理论适用于 nvidia 硬件(大部分内容都集中在合并上,这当然更重要,因为它使自然数据集成为一个不成问题的问题)。然而,根据 ATI 流计算指南,这是 Radeon 卡的情况(对于 5xxx:银行相隔 2kb,并且您要确保将访问(意味着来自所有模拟活动的工作组)平等地分配到所有银行),所以我会想象 NVidia 卡的行为相似。

当然,对于大多数场景而言,全局内存上的银行冲突的可能性是不成问题的,所以在实践中你可以说:

访问全局内存时注意合并 访问本地内存时注意银行冲突 访问寄存器没有问题

【讨论】:

也要注意分区在全局内存中的驻留! 在寻找分区露营的信息时,我偶然发现了这个答案。没错,全局内存在物理上被划分为多个分区,即使合并访问,某些访问模式也可能会产生冲突(请参阅 CUDA SDK 中有关矩阵转置示例的文档)。然而,在 Fermi 架构中,通常对于具有 2.x 计算能力的设备,全局内存访问被缓存,宽度为 32 字节,并且地址被散列,因此理论上分区驻留不应该成为问题。 内存架构在计算能力方面的这些改进 >=2.0 是否也降低了分区驻留对全局内存写入的影响? 运行 CUDA 5 矩阵转置示例,分区露营似乎不会影响 C2050 Tesla(计算能力 2.0),因为粗粒度和细粒度伪转置之间几乎没有任何区别。但我想要官方确认。 是否有关于 Fermi 架构中地址散列的官方文档?【参考方案3】:

多个线程访问同一个银行并不一定意味着存在银行冲突。如果线程想同时从同一个 bank 中的 A DIFFERENT ROW 读取,则会发生冲突。

【讨论】:

以上是关于为啥 Cuda/OpenCL 的全局内存中没有银行冲突?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在没有 CUDA/OpenCL 等的情况下使用 GPU 进行光线追踪?

GPU 中的并行性 - CUDA / OpenCL

用于光线追踪器的 OpenGL 或 CUDA/OpenCL? [关闭]

将数据从全局加载到共享内存时如何避免银行冲突

银行冲突CUDA共享内存?

合并与银行冲突(Cuda)