我有一个应用程序,其中96%的时间用于3D纹理内存插值读取(图中的红点)。
我的内核被设计为在任意穿过纹理存储器的行上执行1000次内存读取,每行一个线程(蓝线)。这条线密集,彼此非常接近,几乎平行的方向行进。
图像显示了我所说的概念。想象一下,图像是来自3D纹理存储器的单个“切片”,例如, z=24
。对所有z
重复该图像。
目前,我正在一行接一个地执行线程,但我意识到如果我在同一块中调用相邻行,我可能能够从纹理存储器局部性中受益,从而减少了内存读取的时间。
我的问题是
如果我有3D纹理和线性插值,我怎样才能从数据局部获益最多?通过在2D中的相同块或3D中的相邻行中运行相邻线(3D邻居或每个切片只是邻居)?
缓存的“大”程度(或者如何在规格中检查)?是否加载,例如问的体素和每个方向的+ -50左右?这将与我在每个区块中放置的相邻线的数量直接相关!
插值如何应用于纹理内存缓存?插值是否也在高速缓存中执行,或者插值是否会减少内存延迟,因为它需要在文本内存中完成?
使用NVIDIA TESLA K40,CUDA 7.5,如果有帮助的话。
答案 0 :(得分:4)
随着这个问题逐渐老化,我问的一些问题似乎没有答案,我将基于我构建TIGRE工具箱的研究给出一个基准答案。您可以在Github repo。
中获取源代码由于答案基于工具箱的特定应用,计算机断层扫描,这意味着我的结果不一定适用于使用纹理记忆的所有应用程序。另外,我的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个Xrays将并行计算,在方形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
加速并不是很大但很重要。正如预期的那样,这暗示了高速缓存插入数据所花费的时间是可测量的,因此在设计应用程序时需要注意它。