CUDA,使用2D和3D阵列

Gra*_*ell 1 c++ arrays cuda

在线上有很多关于在CUDA上分配,复制,索引等2d和3d数组的问题。我得到了很多矛盾的答案,因此我试图汇编过去的问题,看看我是否可以提出正确的问题。

第一个链接:https : //devtalk.nvidia.com/default/topic/392370/how-to-cudamalloc-two-Dimension-array-/

问题:分配二维指针数组

用户解决方案:使用mallocPitch

“正确”的低效率解决方案:在for循环中为每行使用malloc和memcpy(荒谬的开销)

“更正确”的解决方案:将其压缩为1d数组的“专业意见”,有评论说没有人关注性能,但在gpu上使用2d指针结构

第二个链接:https : //devtalk.nvidia.com/default/topic/413905/passing-a-multiDimension-array-to-kernel-how-to-allocate-space-in-host-and-pass-to-device -/

问题:在主机上分配空间并将其传递给设备

子链接:https : //devtalk.nvidia.com/default/topic/398305/cuda-programming-and-performance/dynamically-allocate-array-of-structs/

子链接解决方案:在GPU上编码基于指针的结构是一种糟糕的体验,并且效率极低,因此将其压缩为一维数组。

第三个链接:在CUDA中的设备内存上分配2D阵列

问题:分配和传输二维数组

用户解决方案:使用mallocPitch

其他解决方案:将其展平

第四个链接:如何在CUDA中使用2D阵列?

问题:分配和遍历二维数组

提交的解决方案:不显示分配

其他解决方案:压扁

还有许多其他消息来源大多在说同样的话,但是在多个实例中,我看到有关GPU上指针结构的警告。

许多人认为分配指针数组的正确方法是为每行调用malloc和memcpy,但函数mallocPitch和memcpy2D存在。这些功能的效率有所降低吗?为什么这不是默认答案?

二维数组的另一个“正确”答案是将它们压缩成一个数组。我是否应该习惯生活中的事实?我对我的代码非常拘谨,对我来说感觉很不雅。

我正在考虑的另一种解决方案是使使用1d指针数组的矩阵类最大化,但是我找不到实现双括号运算符的方法。

还要根据此链接:将对象复制到设备?

和子链接答案:cudaMemcpy分段错误

这有点浮躁。

我想全部使用CUDA的类都具有2 / 3d数组,将这些转换为CUDA的1d数组会不会有很多开销?

我知道我已经问了很多问题,但总的来说,我应该习惯于将数组压缩为现实,还是可以使用二维分配和复制函数而不会产生不好的开销,例如在for中调用alloc和cpy的解决方案中环?

Rob*_*lla 5

由于您的问题会汇编其他问题的列表,因此我将通过汇编其他答案的列表进行回答。

cudaMallocPitch / cudaMemcpy2D:

首先,cuda运行时API函数像cudaMallocPitchcudaMemcpy2D实际上并不涉及双指针分配或2D(双下标)数组。只需查看文档,并注意函数原型中的参数类型,即可轻松确认。的srcdst参数是单指针参数。它们不能加倍下标或加倍取消引用。对于其他示例用法,是与此有关的许多问题之一。 是一个完整的示例用法。覆盖与相关联的各种构思的另一示例cudaMallocPitch/ cudaMemcpy2d用法是这里。相反,想想这些正确的做法是,他们一起工作音调分配。同样,当您使用循环中的cudaMemcpy2D一组mallocnew或类似操作)操作创建了基础分配时,您将无法使用传输数据。这种主机数据分配结构特别不适用于处理设备上的数据。

一般情况下,动态分配的2D情况:

如果您想了解如何在CUDA内核使用动态分配的二维数组(这意味着你可以使用双下标访问,如data[x][y]),那么cuda标签信息页面包含此“规范”的问题,它是在这里。那里的爪子给出的答案包括适当的机制以及适当的警告:

  • 还有其他的,非平凡的复杂性
  • 由于数据访问需要解引用2个指针而不是1个指针,因此访问通常不如1D访问有效。

(请注意,分配对象数组(其中的对象具有指向动态分配的嵌入式指针)本质上与2D数组概念相同,并且您在问题中链接的示例对此进行了合理的演示)

展平:

如果您认为必须使用常规的2D方法,那么就可以做到这一点(尽管有时人们会为该过程而苦恼!)但是,由于增加的复杂性和降低的效率,此处的典型“建议”是“扁平化”您的存储方法,并使用“模拟” 2D访问。 是讨论“展平”的问题/答案的众多示例之一。

动态分配的一般3D情况:

当我们将其扩展到3个(或更高!)尺寸时,IMO的一般情况变得过于复杂。额外的复杂性应强烈激励我们寻求替代方案。三次下标的一般情况涉及在实际检索数据之前进行3次指针访问,因此效率更低。 是一个完整的示例(第二个代码示例)。

特殊情况:编译时已知的数组宽度:

请注意,当在编译时知道数组尺寸(在2D数组的情况下为width,在3D数组的3个维度中有2个)时,应将其视为特殊情况。在这种情况下,使用适当的辅助类型定义,我们可以“指示”编译器应如何计算索引,并且在这种情况下,我们可以使用双下标访问方式,其复杂度要比一般情况低得多,并且不会造成任何损失指针追逐带来的效率提升。仅需取消引用一个指针即可检索数据(无论数组维数如何,如果在编译时已知n维数组的n-1维,则不考虑数组维数)。这里已经提到的答案中的第一个代码示例(第一个代码示例)给出了在3D情况下的完整示例,此处的答案给出了此特殊情况的2D示例。

双下标主机代码,单下标设备代码:

最后,另一个方法选项允许我们轻松地在主机代码中混合使用2D(双下标)访问,而在设备代码中仅使用1D(单下标,可能带有“模拟2D”访问)。一个可行的例子在这里。通过将基础分配组织为连续分配,然后构建指针“树”,我们可以在主机上启用双下标访问,并且仍然可以轻松地将平面分配传递给设备。尽管该示例未显示,但是可以扩展此方法以基于平面分配和手动创建的指针“树”在设备上创建双下标访问系统,但是这将具有大致相同的问题如上面给出的2D一般动态分配方法:它将涉及双指针(双引用)访问,因此效率较低,并且与在设备代码中使用的构建指针“树”相关联,存在一些复杂性(例如,cudaMemcpy可能需要执行其他操作)。

从以上方法中,您需要选择一种适合您的胃口和需求的方法。没有一个适合所有可能情况的建议。