如何缓存3D纹理内存?

And*_*uri 6 cuda

我有一个应用程序,其中96%的时间花在3D纹理内存插值读取(图中的红点).

我的内核被设计为在任意穿过纹理存储器的行上执行1000次内存读取,每行一个线程(蓝线).这条线密集,彼此非常接近,几乎平行的方向行进.

图像显示了我所谈论的概念.想象一下,图像是来自3D纹理存储器的单个"切片",例如z=24.对所有人重复图像z. 在此输入图像描述

目前,我正在一个接一个地执行线程,但我意识到如果我在相同的块中调用相邻的行,我可能能够从纹理存储器局部性中受益,从而减少了内存读取的时间.

我的问题是

  • 如果我有线性插值的3D纹理,我怎样才能从数据局部获益最多?通过在2D中的相同块或3D中的相邻行中运行相邻线(3D邻居或每个切片只是邻居)?

  • 缓存的"大"程度(或者如何在规范中检查)?是否会加载例如所要求的体素和每个方向的+ -50?这将与我在每个块中放置的相邻线的数量直接相关!

  • 插值如何应用于纹理内存缓存?插值是否也在高速缓存中执行,或者插值是否会减少内存延迟,因为它需要在文本内存中完成?


使用NVIDIA TESLA K40,CUDA 7.5,如果有帮助的话.

And*_*uri 4

由于这个问题已经过时了,而且我提出的一些问题似乎没有答案,我将根据我构建 TIGRE 工具箱的研究给出一个基准答案。您可以在Github 存储库中获取源代码。

由于答案基于工具箱的特定应用程序(计算机断层扫描),这意味着我的结果不一定适用于所有使用纹理内存的应用程序。此外,我的 GPU(见上文)相当不错,因此不同硬件的性能可能会有所不同。


具体细节

值得注意的是:这是一个锥束计算机断层扫描应用程序。这意味着:

  • 这些线条或多或少均匀地沿着图像分布,覆盖了图像的大部分
  • 这些线或多或少与相邻线平行,并且主要总是在单个平面中。例如,它们总是或多或少是水平的,而不是垂直的。
  • 线条顶部的采样率是相同的,这意味着相邻的线条将始终对彼此非常接近的下一个点进行采样。

所有这些信息对于内存局部性都很重要。

此外,正如问题中所述,内核 96% 的时间是内存读取,因此可以安全地假设报告的内核时间的变化是由于内存读取速度的变化造成的。


问题

如果我有带有线性插值的 3D 纹理,我如何才能从数据局部性中获得最大收益?通过运行 2D 中同一块中的相邻线或 3D 中的相邻线(3D 邻居或每个切片的邻居)?

一旦对纹理内存有了更多的经验,就会发现简单的答案是:将尽可能多的相邻行一起运行。图像索引中的内存读取彼此越接近越好。

这对于断层扫描来说有效意味着运行方形探测器像素块。将光线(原始图像中的蓝线)打包在一起。

缓存有多大(或者我如何在规格中检查它)?它是否会加载例如所要求的体素及其周围各个方向的+-50?这将与我在每个块中放置的相邻线的数量直接相关!

虽然不可能说,但根据经验,我发现运行较小的块更好。我的结果显示,对于 512^3 图像、具有 512^2 条光线、采样率为 ~2 个样本/体素的块大小:

32x32 -> [18~25] ms
16x16 -> [14~18] ms
8x8   -> [11~14] ms
4x4   -> [25~29] ms
Run Code Online (Sandbox Code Playgroud)

块大小实际上是一起计算的方形相邻光线的大小。例如,32x32 意味着将并行计算 1024 个 X 射线,这些 X 射线在 32x32 方形块中彼此相邻。由于每行执行完全相同的操作,这意味着样本是在图像上的 32x32 平面上采集的,覆盖大约 32x32x1 索引。

可以预见的是,在减小块大小的某个时刻,速度会再次变慢,但这(至少对我来说)是令人惊讶的低值。我认为这暗示内存缓存从图像中加载相对较小的数据块。

此结果显示了原始问题中未询问的附加信息:有关速度的越界样本会发生什么情况。添加任何if条件都会显着减慢速度,因此我对内核进行编程的方式是在确保位于图像之外的行中的点开始采样,并在类似情况下停止。这是通过在图像周围创建一个虚构的“球体”来完成的,并且始终采样相同的数量,而与图像和线条本身之间的角度无关。

如果您看到我显示的每个内核的时间,您会注意到所有这些都是[t ~sqrt(2)*t],而且我已经检查过,确实更长的时间是从线条和图像之间的角度是 45 度的倍数开始的,其中更多样本落在图像(纹理)内。

这意味着图像索引 () 的采样在计算上tex3d(tex, -5,-5,-5)是免费的。没有时间花在越界阅读上。读取大量越界点比检查这些点是否落在图像内更好,因为这种情况会减慢内核速度,并且越界采样的成本为零。if

插值如何应用于纹理内存缓存?插值是否也在缓存中执行,或者插值会减少内存延迟,因为它需要在文本内存本身中完成?

为了测试这一点,我运行了相同的代码,但使用了线性插值 ( cudaFilterModeLinear) 和最近邻插值 ( cudaFilterModePoint)。正如预期的那样,当添加最近邻插值时,速度会有所提高。对于8x8具有前面提到的图像尺寸的块,在我的电脑中:

Linear  ->  [11~14] ms
Nearest ->  [ 9~10] ms
Run Code Online (Sandbox Code Playgroud)

加速虽然不是很大,但很显着。正如预期的那样,这暗示缓存插入数据所花费的时间是可测量的,因此在设计应用程序时需要注意这一点。