在 CUDA 中传递和操作多维数组是不可能的,还是出于性能原因没有这样做?

问题描述 投票:0回答:1

只要有人在 StackOverflow 或 nVidia 论坛上询问有关 CUDA 中的多维数组的问题,答案或多或少如下所示:

请将多维数组展平为一维。

这是一个示例解决方案:

//Implementation of a CUDA program using a 1D array.
... ... ...

我对此感到困惑

在 CUDA 中传递和操作多维数组是不可能的,还是出于性能原因没有这样做?

multidimensional-array cuda
1个回答
0
投票

C++ 中的多维数组定义属于两类之一。

第一类是当编译器知道或可以在编译时发现数组宽度。在这种情况下,对于多订阅访问,例如

array[x][y]
,编译器知道
array
的宽度(即对应于第二个下标的维度中的元素数),并且“在引擎盖下将生成这样的索引:

*(a+x*awidth+y)

所有这些项的含义与它们在 C++ 中的含义完全相同:

a
是数组的“名称”,在执行此类指针运算时会衰减为指针,
x
是第一个下标,
y
是第二个下标,
awidth
是编译器在编译时为
a
发现的宽度。根据 C++ 定义,
a
是一个 array 而不是其他任何东西。

在这种情况下,当允许“衰减”到指针时,

a
将指向一个位置,该位置不包含指针,但包含与数组
a
相同类型的元素。

在这个类别中,只生成一次对内存的访问,它检索有问题的

a
的元素。我会称之为“有效”案例,尽管这是主观的,所以我不想争论它。

另一种类型的多下标数组可以使用“指针追逐”来构造。在这种情况下,

a
不是一个array,它是一个双指针:

T **a;

在这种情况下,

a
指向一个位置,该位置包含指向数组
a
元素类型的指针(更典型的是,
a
指向“行指针”数组的第一个元素),因此,我将
a
称为“双指针”,并且正式地,
a
没有命名 array。但是,在典型用法中,我们可以使用相同的语法引用
a
的元素:
a[x][y]
。 “幕后”的编译器不会生成我们之前介绍的指针算法,而是生成两个连续的指针取消引用操作:

  1. 取消引用
    a
    (已经是一个指针)以检索另一个指针。
  2. 取消引用在步骤 1 中检索到的指针,以检索所需的元素
    a

在上面我们看到需要two内存操作。第一个检索指针,第二个检索有问题的元素。

我称之为“低效”方法。第一种方法正式使用“数组”,第二种方法不是,但在典型的双下标用法中,它们在语法上看起来是相同的。

所有这些讨论都适用于 C++ 编译器行为,并不是 CUDA 独有或特定的。

如果你小心翼翼地让 CUDA 编译器可以在编译时发现你的数组宽度(并非总是可能),那么它在指针算法、指针解引用和涉及的内存操作数方面的行为是相同的,因为我对上面第一个案例的描述。否则,如果您使用第二种方法声明您的“数组”,每次访问将获得多个内存操作(当然,编译器也可能缓存第一个解引用操作的结果,这可能会提供一些好处,但这不是保证的行为在所有情况下,并且只在某些重用的情况下提供好处,而不是在一般情况下。)

这个答案 提供了对 CUDA 中 2D 和 3D 示例的调查,以及一些权衡的讨论。

对于正在或必须使用第二种类型的人来说,展平数组总是可行的,使用展平的替代方案将导致一种访问方法,其行为大致与上面的第一种方法相同:只需要一次访问,使用典型的指针算术。

© www.soinside.com 2019 - 2024. All rights reserved.