【问题标题】:cuda memory alignmentcuda内存对齐
【发布时间】:2012-09-28 12:57:28
【问题描述】:

在我的代码中,我使用结构是为了便于将参数传递给函数(我不使用结构数组,而是使用一般的数组结构)。 当我在 cuda-gdb 中并检查内核中的点时,我将值赋予一个简单的结构,例如

struct pt{
int i;
int j;
int k;
}

即使我没有做复杂的事情,而且很明显成员应该有指定的价值观,但我得到......

要求栈的位置0,栈上只有0个元素。

所以我在想,即使它不是数组,也可能此时内存的对齐存在问题。所以我把头文件中的定义改成

struct __align__(16) pt{
int i;
int j;
int k;
}

但是,当编译器尝试编译使用相同定义的主机代码文件时,会出现以下错误:

错误:预期在数字常量错误之前的 unqualified-id:预期 数字常量错误之前的“)”:预期的构造函数,析构函数, 或';'标记之前的类型转换

那么,我应该对主机和设备结构有两种不同的定义吗???

进一步我想问一下如何概括对齐的逻辑。我不是计算机科学家,因此编程指南中的两个示例无法帮助我了解全局。

比如下面两个应该怎么对齐?或者,应该如何对齐具有 6 个浮点数的结构?还是4个整数?同样,我没有使用这些数组,但我仍然在内核或_设备_函数中使用这些结构定义了很多变量。

struct {
    int a;
    int b;
    int c;
    int d;
    float* el;    
} ;

 struct {
    int a;
    int b
    int c
    int d
    float* i;
    float* j;
    float* k;
} ;

提前感谢您的任何建议或提示

【问题讨论】:

标签: cuda


【解决方案1】:

这篇文章有很多问题。由于 CUDA 编程指南很好地解释了 CUDA 中的对齐方式,因此我将仅解释指南中不明显的一些内容。

首先,你的主机编译器给你错误的原因是因为主机编译器不知道__align(n)__是什么意思,所以它给出了一个语法错误。您需要在项目的标题中添加类似以下内容。

#if defined(__CUDACC__) // NVCC
   #define MY_ALIGN(n) __align__(n)
#elif defined(__GNUC__) // GCC
  #define MY_ALIGN(n) __attribute__((aligned(n)))
#elif defined(_MSC_VER) // MSVC
  #define MY_ALIGN(n) __declspec(align(n))
#else
  #error "Please provide a definition for MY_ALIGN macro for your host compiler!"
#endif

那么,我应该对主机和设备结构有两种不同的定义吗?

不用,就用MY_ALIGN(n),像这样

struct MY_ALIGN(16) pt { int i, j, k; }

比如下面两个应该怎么对齐?

首先,__align(n)__(或任何主机编译器风格)强制结构的内存从内存中的地址开始,该地址是n 字节的倍数。如果结构的大小不是n 的倍数,那么在这些结构的数组中,将插入填充以确保每个结构正确对齐。要为n 选择合适的值,您需要最小化所需的填充量。如编程指南中所述,硬件要求每个线程读取与 1、2、4、8 或 16 字节对齐的字。所以……

struct MY_ALIGN(16) {
  int a;
  int b;
  int c;
  int d;
  float* el;    
};

在这种情况下,假设我们选择 16 字节对齐。在 32 位机器上,指针占用 4 个字节,因此结构占用 20 个字节。 16 字节对齐将浪费每个结构 16 * (ceil(20/16) - 1) = 12 字节。在 64 位机器上,由于 8 字节指针,每个结构只会浪费 8 个字节。我们可以改用MY_ALIGN(8) 来减少浪费。权衡是硬件将不得不使用 3 个 8 字节加载而不是 2 个 16 字节加载来从内存加载结构。如果您没有受到负载的限制,这可能是一个值得权衡的选择。请注意,您不想为此结构对齐小于 4 个字节。

struct MY_ALIGN(16) {
  int a;
  int b
  int c
  int d
  float* i;
  float* j;
  float* k;
};

在这种 16 字节对齐的情况下,在 32 位机器上每个结构只浪费 4 个字节,在 64 位机器上浪费 8 个字节。它需要两个 16 字节的加载(或 64 位机器上的 3 个)。如果我们对齐到 8 个字节,我们可以通过 4 字节对齐(在 64 位机器上为 8 个字节)完全消除浪费,但这会导致负载过大。再次,权衡。

或者,一个有 6 个浮点数的结构应该如何对齐?

再次,权衡:要么每个结构浪费 8 个字节,要么每个结构需要两次加载。

还是 4 个整数?

这里没有权衡。 MY_ALIGN(16).

同样,我没有使用这些数组,但我仍然在内核或_设备_函数中使用这些结构定义了许多变量。

嗯,如果您不使用这些数组,那么您可能根本不需要对齐。但是你是如何分配给他们的?正如您可能看到的那样,所有这些浪费都值得担心——这是另一个支持数组结构而不是结构数组的好理由。

【讨论】:

  • 非常感谢您的回答。我最多希望有一个指向外部参考的链接,而这更多的是关于对齐的完整课程。我感到受宠若惊。我的代码确实使用了数组结构。我在较小的范围内使用像 pt(上面提到的)这样的结构,以方便将参数从内核中传递到被调用的 _ 设备 _ 函数。当我尝试从 cuda-gdb 查询它们的值时,它们就像是不可见的。
  • 很高兴为您提供帮助。赞成票不会出错。 :) 不确定这是否有助于解决 cuda-gdb 问题。以我的经验,设备代码调试器并不总是显示所有值——只有那些立即在范围内/活动的值,以及当前暂停的代码位置。
  • 所以,为了明确思路:当我收到“要求堆栈的位置 0 时,堆栈上只有 0 个元素。”由 gdb ......这意味着 gdb 没有让我可以使用该值来查询(?),或者该变量尚未定义,和/或没有为其指定值?我更关心程序执行本身发生的事情,而不是我通过 gdb 看到的。
  • 我不熟悉那个错误。您是否使用 -G 和 -g 编译了 .cu 文件以便使用 cuda-gdb 进行调试?
  • 多年后,仍然是一个很好的答案!值得一提的是,英特尔的编译器也使用了__declspec(align(n))。编译器定义了__INTEL_COMPILER,所以你可以将它添加到你的MSVC 之一。并且 Clang 定义了__clang__,并使用了 GCC 的版本 (__attribute__((aligned(n)))),所以你可以在那里添加它。这几乎涵盖了我必须处理的所有主要(非专业,即我一无所知的ARM)编译器;)我在英特尔遇到了麻烦,因为它们定义了一堆你不会想到的东西。具体的我不记得了,但解决办法是先检查一下。
【解决方案2】:

现在,您应该使用 C++11 alignas specifierg++ 支持(包括与当前 CUDA 兼容的版本),以及 nvcc 支持的 IIANM。这应该让您无需求助于宏。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-28
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
相关资源
最近更新 更多