CUDA共享内存原子操作中哪个更快 - 线程束局部性还是反局部性?

3
假设许多(CUDA内核网格)块中的线程正在重复更新相当数量的共享内存位置。

在以下哪种情况下,这样的工作会更快完成?:
  1. 内部线程束访问局部性的情况,例如每个线程束访问的内存位置总数很小,并且其中大部分确实由多个lane访问。
  2. 访问反局部性的情况,其中所有lane通常访问不同的位置(并且可能会尽力避免银行冲突)?
而且同样重要的是 - 这是与微体系结构有关,还是在所有最近的NVIDIA微体系结构上基本相同?
3个回答

3

反本地化访问将更快。

在SM5.0(Maxwell)及以上的GPU上,共享内存原子操作(假设添加)由于地址冲突(两个具有相同地址的通道)而需要重播指令。普通的Bank冲突重播也适用。在Maxwell / Pascal上,共享内存单元在两个SM分区之间固定轮换访问(每个分区有2个调度程序)。对于每个分区,共享内存单元将完成所有的指令重播,然后再移动到下一个指令。Volta SM将在任何其他共享内存指令之前完成该指令。

  1. 避免Bank冲突
  2. 避免地址冲突

在Fermi和Kepler架构中,在读取修改写入操作之前必须执行共享内存锁定操作。这会阻塞所有其他线程指令。

Maxwell和更新的GPU具有比Fermi / Kepler更快的共享内存原子性能。

可以编写非常简单的内核来微基准测试您的两种不同情况。CUDA分析器提供了共享内存访问的指令执行计数和重播计数,但不区分由于原子操作和由于负载/存储冲突或向量访问而重播的指令。


所以,你的意思是说,在同一个时钟周期内,即使涉及到共享内存中不相交的值和不同的bank,来自不同warp的线程的共享内存操作也永远不会并发执行?因此,反局部动作总是更优的? - einpoklum
3
每个共享内存单元(每个SM)每个周期可以执行1个操作(读或写)。所有地址和数据都来自一个warp指令(不会合并来自不同warp的指令)。由于访问大小(>32位)、银行冲突、地址冲突(原子操作)或操作是需要读取/操作/写入的原子操作,指令可能需要多个操作。 - Greg Smith
你如何知道这是事实? - einpoklum
2
@einpoklum: 如果Greg说是这样的,那就是这样。 - talonmies
@talonmies: 但我记得共享内存操作的延迟比那个高一个数量级。或者- Greg是在谈论吞吐量吗?如果是这样,那么后续操作之间不会存在竞争吗? - einpoklum
共享内存单元的吞吐量为每个周期1次读取或1次写入操作。由于数据宽度、银行冲突、地址冲突或原子操作(多操作),一条指令可能需要多个操作。在Maxwell/Pascal及以上架构中,延迟是指从指令队列到共享内存单元的时间+操作数量*2个周期(Maxwell/Pascal在偶数和奇数SM分区之间进行时间切片操作)或1个周期(Volta不进行时间切片)+写回到寄存器文件。由于队列深度的不同,延迟也会有所变化。为了得到更好的答案,您需要明确后续操作的情况。 - Greg Smith

1

即使不需要了解CUDA硬件中如何实现共享内存原子操作,也可以提出一个非常简单的论点:归根结底,原子操作必须以某种方式在某个时刻进行序列化。这在一般情况下都是正确的,无论你在哪个平台或硬件上运行。由于原子性的本质特征,这是必须的。如果你有多个原子操作并行发出,你必须以某种方式执行它们以确保原子性。这意味着,无论是GPU还是CPU,随着争用的增加,原子操作总是会变得更慢。唯一的问题是:减慢的程度。这取决于具体的实现。

因此,通常情况下,您希望尽可能地降低争用级别,即尝试以并行方式在同一内存位置上执行原子操作的线程数。


0

这只是一个推测性的部分答案。

考虑相关问题:共享内存上原子操作的性能及其被接受的答案

如果那里的被接受的答案是正确的(即使到今天仍然正确),那么在更局部化的访问中,warp线程会相互干扰,使得许多lane进行原子操作变慢,即提高了warp原子操作的反局部性。

但说实话 - 我不确定我完全赞同这种论证方式,也不知道自那个答案写下以来是否有所改变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接