我正在开发一个使用卡上所有可用内存的CUDA应用程序,并试图找出减少缓存未命中的不同方法。
问题域由大型2或3-D网格组成,具体取决于要解决的问题类型。 (对于那些感兴趣的人,它是FDTD模拟器)。每个元素依赖于“并行”数组中的两个或四个元素(即,几乎相同维度的另一个数组),因此内核必须访问三个或六个不同的数组。
问题
*希望这不是“过于本地化”。随意编辑问题
三个数组之间的关系可以看作是(为平庸的ASCII艺术道歉)
A[0,0] -C[0,0]- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B[0,0] B B B
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B B B B
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B B B B[3,2]
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A[3,3]
[2,3]
通过线连接的项目是耦合的。如上所示,A[]
取决于B[]
和C[]
,而B[]
仅取决于A[]
,C[]
也是如此。所有A[]
都在第一个内核中更新,所有B[]
和C[]
都会在第二次更新时更新。
如果我将这些数组声明为简单的2D数组,我最终会使用跨步内存访问。对于非常大的域大小(上面的网格中为3x3 + 1),这会导致占用率和性能不足。
所以,我考虑在Z顺序曲线中重新排列数组布局:
此外,将这些交织成一个数组是相当简单的,这应该提高获取性能,因为(取决于交错顺序)给定单元更新所需的至少一半元素将彼此接近。但是,我不清楚GPU在访问多个阵列时是否使用多个数据指针。如果是这样,这种想象的好处实际上可能是一种障碍。
问题
我已经读过NVidia在使用纹理内存或cudaArray
时在幕后自动执行此操作。如果不是这种情况,我是否应该期望在跨越大跨度时(当Z曲线在高细分级别从右上角到左下角时)延迟增加,以消除较小网格中局部性的好处?
将网格划分为可以放入共享内存的较小块应该会有所帮助,而Z顺序使得这相当简单。我应该有一个单独的内核传递来更新块之间的边界吗?与我期望的节省相比,启动另一个内核的开销是否显着?
使用2D vs 1D阵列有什么好处吗?我希望记忆是线性的,但我不确定在CUDA文献中经常使用的2D内存布局隐喻是否有任何实际意义。
答案 0 :(得分:1)
只需将其从未答复的列表中删除即可:
经过大量的基准测试和不同的安排后,我发现最快的方法是使数组以z顺序交错,以便线程所需的大多数值在RAM中彼此靠近。这样改善了缓存行为(从而提高了性能)。显然,在许多情况下,Z阶无法将所需值保持在一起。我想知道是否旋转象限以减小Z末端和下一个象限之间的“距离”,但是我还没有尝试过。
感谢大家的建议。