我正在开发一个利用卡上所有可用 RAM 的 CUDA 应用程序,并试图找出减少缓存未命中的不同方法。
问题域由一个大的 2 维或 3 维网格组成,具体取决于要解决的问题的类型。(对于那些感兴趣的人,它是一个 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 与 1D 阵列有什么真正的好处吗?我希望内存是线性的,但不确定 CUDA 文献中经常使用的 2D 内存布局隐喻是否有任何实际意义。
哇 - 很长的问题。感谢您阅读和回答任何/所有这些。