又一个CUDA纹理内存线程(为什么Fermi上的纹理内存会更快?)

4
有很多stackoverflow的帖子询问为什么使用纹理的内核不比使用全局内存访问的内核更快。答案和评论对我来说似乎总是有点深奥。NVIDIA Fermi架构白皮书黑白分明地说明:Fermi架构通过为加载和存储实现单一统一内存请求路径(每个SM多处理器带有一个L1缓存和服务于所有操作(加载、存储和纹理)的统一L2缓存),解决了这一挑战。那么,为什么人们期望在Fermi设备上使用纹理内存会加速呢?因为对于每次内存获取(无论是否绑定到纹理),都会使用相同的L2高速缓存,所以实际上,对于大多数情况,直接访问全局内存应该更快,因为它也通过L1缓存进行缓存,而纹理获取则没有。这也在stackoverflow的一些相关问题中报道过。有人可以证实这一点或告诉我我错过了什么吗?

每个流多处理器上都有一个纹理缓存。这个缓存可以更好地处理2D访问的数据局部性,例如在有限差分方法中进行模板计算。纹理内存确实比全局内存访问稍微快一些,全局内存也有缓存但使用不同的机制。有关某些时序方面的信息,请参见我的答案Is 1D texture memory access faster than 1D global memory access? - Vitality
@JackOLantern你想提供一个答案吗?我会点赞的。任何来自纹理的访问加速都是由于纹理缓存而产生的,而OP似乎忽略了这一点。 - Robert Crovella
@RobertCrovella 谢谢您,Robert。 - Vitality
3个回答

10
您忽略了每个流处理器都有一个纹理缓存(请参见下图,该图说明了Fermi的流处理器)。
纹理缓存与L1 / L2缓存具有不同的含义,因为它针对数据局部性进行了优化。数据局部性适用于所有情况,即需要访问关于正则、笛卡尔、1D、2D或3D网格的语义邻近点的数据。为了更好地解释这个概念,请考虑以下图示有限差分计算中所涉及的模板的图形。
在红点处计算有限差分涉及到访问与蓝点相关联的数据。现在,这些数据不是红点的物理邻居,因为当将2D或3D数组展平为1D时,它们不会被物理上连续地存储在全局内存中。但是,它们是红点的语义邻居,而纹理内存非常擅长缓存这些值。另一方面,当必须频繁访问相同的数据或其物理邻居时,L1 / L2缓存非常好。
奖章的另一面是,与L1 / L2缓存相比,纹理缓存具有较高的延迟,因此,在某些情况下,不使用纹理可能不会导致性能显着恶化,仅仅因为L1 / L2缓存机制。从这个角度来看,纹理在早期的CUDA架构中非常重要,当时全局内存读取没有被缓存。但是,正如在Is 1D texture memory access faster than 1D global memory access?中所证明的那样,对于Fermi来说,纹理内存是值得使用的。

非常感谢您的出色回答!您能告诉我在哪里可以找到这样的信息吗?在发布之前,我阅读了 Fermi 架构的白皮书,但没有提到特殊的硬件纹理缓存,并且 SM 的图片直接被切割在“Uniform Cache”下面。在编程指南中,有一个相当大的章节介绍如何使用纹理,但是关于 Fermi 卡上的特殊硬件缓存却没有任何说明。加上“并统一 L2 缓存服务所有操作(加载、存储和纹理)”这句话,让我得出了显然非常错误的结论。 - betapatch
好的,我再次阅读了链接的白皮书,必须承认我仍然不确定Fermi是否真的存在硬件纹理缓存。从白皮书中可以看出,纹理使用L2缓存,但具有不同的内存访问方案。如果有人能对此发表评论,那就太好了。请原谅我还没有关闭这个问题!另外,JackOLantern的答案非常好,它没有评论链接的白皮书,也没有回答在Fermi上是否真的有一个独立的硬件缓存。 - betapatch
请原谅我不相信。让我相信的那一点信息可以在当前编程指南(6.5)的第181页找到。 - betapatch

3

如果通过纹理读取的数据是二维或三维的话,使用CUDA数组的块线性布局通常比使用pitch-linear布局更好,因为缓存行包含2D或3D数据块而不是行。

但即使对于一维数据,纹理缓存也可能会补充其他芯片上的缓存资源。如果内核只使用全局内存访问而没有纹理加载,那么所有的内存流量都经过每个SM的L1缓存。如果一些内核输入数据通过纹理读取,则每个SM的纹理缓存将减轻L1的一些压力,并使其能够处理原本会进入L2的内存流量。

在做出这些折衷时,要注意从一个芯片架构到另一个芯片架构,NVIDIA所做出的决策非常重要。Maxwell中的纹理缓存与L1共享,这使得从纹理中读取不太理想。


+1. 我认为,也许从 Kepler 架构开始,并引入只读缓存纹理不是很理想的。我的(有限的)经验表明,现在纹理主要用于“向后兼容”,也许就像提到的那样,对于许多情况而言,这个游戏已经不值得一试了。 - Vitality
我不确定你所说的“只读缓存纹理”是什么意思,纹理缓存始终是只读的。在SM 3.5中添加的LDG指令是通过纹理进行读取的包装器,无需绑定纹理内存,但它们仍通过纹理缓存。如果您不使用核心纹理功能(格式转换、双/三线性插值等),那么通过纹理进行读取并不一定会胜过其他方式。 - ArchaeaSoftware
1
抱歉,我想我使用了错误的标点符号,在“cache”和“texture”之间缺少了逗号,并忘记提及绑定。无论如何,我确切地意味着,以错综复杂的方式,你在评论中更明确地解释了我的句子应该被理解为:“......和只读缓存的引入,纹理绑定[在核心纹理功能方面]是不太理想的”。 - Vitality

1
我不会忽视纹理内存的使用。例如,论文“在GPU寄存器中实现通信最小化的2D卷积”(http://parlab.eecs.berkeley.edu/publication/899)比较了不同的小型2D卷积实现,并且他们认为直接从纹理内存加载到寄存器的策略是一个好方法。

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