假设我有一个数据数组,例如一个大小为 N 的 3D 向量数组。假设我的 SYCL 内核的每次迭代都专门或主要只关注一个向量。作为一般规则,以下哪一种将其分解为连续缓冲区的方法更有效——或者它是否重要?
我意识到目标设备对此有很大影响,所以让我们假设它是一个独立的 GPU(即数据确实必须复制到不同的内存芯片,并且该设备没有像 FPGA 这样的疯狂架构——我是主要针对通过 CUDA 的 GTX 1080,但我希望当代码编译到 OpenCL 或我们使用另一个现代 GPU 时答案可能相似。
- 为每个坐标创建一个单独的缓冲区,例如
sycl::buffer<float> x, y, z;
每个大小为 N 的缓冲区。这样访问它们时,我可以使用sycl::id<1>
传递给我的内核 lambda 作为索引而无需数学。(我怀疑编译器可能会对此进行优化。) - 为所有这些创建一个打包缓冲区,例如
sycl::buffer<float> coords;
大小为 3N。当使用sycl::id<1>
被调用访问它们时i
,我将 x 坐标设为buffer_accessor[3*i]
,将 y 坐标设为buffer_accessor[3*i+1]
,将 z 坐标设为buffer_accessor[3*i+2]
。(我不知道编译器是否可以对此进行优化,我不确定对齐问题是否会起作用。) - 使用结构创建一个解压缩缓冲区,例如
struct Coord { float x,y,z; }; sycl::buffer<Coord> coords;
. 由于对齐填充,内存使用量增加了相当惊人的成本,在这个例子中增加了 33%——这也会增加将缓冲区复制到设备所需的时间。但权衡是您可以在不操作 的情况下访问数据sycl::id<1>
,运行时只需处理一个缓冲区,并且设备上不应该出现任何缓存行对齐效率低下的问题。 - 使用大小为 (N,3) 的二维缓冲区并仅在第一个维度的范围内进行迭代。这是一个不太灵活的解决方案,我不明白为什么在不迭代所有维度时要使用多维缓冲区,除非为此用例内置了很多优化。
我找不到任何关于数据架构的指南来获得对这类事情的直觉。现在(4)看起来很傻,(3)涉及不可接受的内存浪费,我正在使用(2)但想知道我是否不应该使用(1)来避免 id 操作和 3*sizeof(float)对齐的访问块。