2

我正在开发一个利用卡上所有可用 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 阶曲线中重新排列数组布局:

Z 阶空间填充曲线

此外,将它们交错到一个数组中是相当简单的,这应该会提高获取性能,因为(取决于交错顺序)给定单元更新所需的至少一半元素将彼此接近。但是,我不清楚 GPU 在访问多个数组时是否使用多个数据指针。如果是这样,这种想象中的好处实际上可能是一个障碍。

问题

我读过 NVidia 在使用纹理内存或cudaArray. 如果不是这种情况,我是否应该期望在跨越大跨度时增加延迟(当 Z 曲线在高细分级别从右上角到左下角时)以消除较小网格中局部性的好处?

  1. 将网格划分为可以放入共享内存的较小块肯定会有所帮助,而 Z 顺序使这变得相当简单。我应该有一个单独的内核通道来更新块之间的边界吗?与我预期的节省相比,启动另一个内核的开销会很大吗?

  2. 使用 2D 与 1D 阵列有什么真正的好处吗?我希望内存是线性的,但不确定 CUDA 文献中经常使用的 2D 内存布局隐喻是否有任何实际意义。

哇 - 很长的问题。感谢您阅读和回答任何/所有这些。

4

1 回答 1

2

只是为了将其从未答复的列表中删除:

经过大量的基准测试和不同的安排,我发现最快的方法是让数组按 z 顺序交错,这样线程所需的大部分值在 RAM 中彼此靠近。这改进了缓存行为(以及性能)。显然,在许多情况下,Z 顺序未能将所需值保持在一起。我想知道旋转象限是否可以减少 Z 末端和下一个象限之间的“距离”,但我没有尝试过。

感谢大家的建议。

于 2020-09-22T13:59:50.700 回答