【问题标题】:How is 3D texture memory cached?3D 纹理内存是如何缓存的?
【发布时间】:2016-03-11 10:20:53
【问题描述】:

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

我的内核设计为在任意穿过纹理内存的行上进行 1000 次内存读取,每行一个线程(蓝线)。这些线路密密麻麻,彼此非常接近,几乎平行移动。

图片显示了我所说的概念。想象图像是来自 3D 纹理内存的单个“切片”,例如z=24。对所有z 重复该图像。

目前,我只在一行之后执行线程,但我意识到如果我在同一块中调用相邻行,我可能能够从纹理内存局部性中受益,从而减少内存读取时间。

我的问题是

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

  • 缓存有多大(或者我如何在规范中检查)?它是否加载例如问的体素和各个方向的+-50?这将与我在每个块中放置的相邻行的数量直接相关!

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


在 NVIDIA TESLA K40、CUDA 7.5 上工作,如果有帮助的话。

【问题讨论】:

  • 这听起来确实是一个最好使用基准测试解决的问题。我不相信您感兴趣的任何信息都是公开的,除了缓存大小。
  • @talonmies 我看到了。不过,这是一个合理的答案。我花了几个小时试图找到信息,我想我不是唯一一个。
  • 只有基准测试才能说明问题。但根据我的经验,绝对值得尝试同时推进所有“线路”并一起查询靠近的位置。
  • 问一个有点“酸甜苦辣”的问题——这已经快一年了。你有没有得出任何结论?您是否可以根据您为 TIGRE 工具箱编写的代码的基准测试/测试添加一个摘要答案?
  • 谢谢你这样做,我认为有一个部分答案总比没有好

标签: cuda


【解决方案1】:

由于这个问题越来越老了,而且我提出的一些问题似乎没有答案,我将根据我构建 TIGRE 工具箱的研究给出一个基准答案。可以在Github repo获取源码。

由于答案基于工具箱的特定应用程序,计算机断层扫描,这意味着我的结果不一定适用于所有使用纹理内存的应用程序。此外,我的 GPU(见上文)是一个相当不错的 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

块大小实际上是一起计算的正方形相邻射线的大小。例如。 32x32 意味着将并行计算 1024 条 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

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

【讨论】:

  • 我已经接受了我自己的答案,但如果有人对此有更深入的了解,请随时发布另一个答案,我很乐意考虑更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-18
  • 2012-01-13
  • 1970-01-01
  • 2012-12-01
  • 2018-01-21
  • 2010-12-29
  • 1970-01-01
相关资源
最近更新 更多