C ++和CUDA中结构的不同大小

Different sizes for a struct in cpp and CUDA

我在使用内核时遇到了一些问题,该内核使用了我在 C++ 中定义的一些结构。 cuda-memcheck 给我的错误是对齐问题。

我尝试使用的结构包含一些指针,我猜这些指针给我带来了问题。我已经打印以控制 C++ 端和 CUDA 端的结构大小,包括 .cu 文件和内核中的主机函数。这给出了不同的结果,这解释了我所看到的问题,但我不确定为什么会发生,也不知道如何解决。

我使用的结构如下

struct Node {};
struct S
{
    Node *node0;
    Node *node1;
    Node *node2;
    double p0;
    double p1;
    double p2;
    double p3;

    Eigen::Matrix<double, 3, 2> f1;
    Eigen::Matrix<double, 3, 2> f2;
}

这在 C++ 中大小为 160 字节,但在 CUDA 中为 152 字节。为了传输数据,我正在分配一个 CUDA 端缓冲区并执行 cudaMemcpy

std::vector<S> someVector; // Consider it exists
S *ptr;
cudaMalloc(&ptr, sizeof(S) * someVector.size());
cudaMemcpy(ptr, someVector.data(), sizeof(S)*someVector.size(), cudaMemcpyHostToDevice);

我猜这是错误的,因为 CUDA 和 C++ 中的大小不同。

当我尝试访问内核中的 S::node0S::node1S::node3 时,我收到未对齐的访问错误。

关于这个问题我有三个问题:

编辑: 感谢接受的答案,我能够理解我遇到的问题的原因。 Eigen 尽可能使用 vectorizacion 并为此请求 16 字节对齐。当 Eigen 对象大小是 16 字节的倍数时,启用矢量化。在我的特殊情况下,两个 Eigen::Matrix<double, 3,2> 对矢量化有效。

然而,在 CUDA 中,Eigen 不要求 16 字节对齐。

因为我的结构有 4 个双精度数和 3 个指针,占 56 个字节,不是 16 的倍数,所以在 CPU 中它必须添加 8 个填充字节,因此特征矩阵是 16 字节对齐。在 CUDA 中不会发生这种情况,因此大小不同。

我实现的解决方案是手动添加8个填充字节,所以结构在CPU和CUDA中是相同的。这解决了问题并且不需要禁用矢量化。我发现另一个可行的解决方案是将 Eigen::Matrix<double,3,2> 更改为 2 Eigen::Matrix<double,3,1>Eigen::Matrix<double,3,1>不满足矢量化要求,因此不需要在CPU中添加8个填充字节。

这种差异是由于 Eigen 在 C++ 和 CUDA 中请求内存对齐的方式。

在 C++ 中,S 被对齐为 16 字节(您可以检查 alignof(S) == 16)。这是由于 Eigen 的矩阵与 16 字节对齐,可能是因为使用了需要这种对齐的 SSE 寄存器。您的其余字段对齐到 8 字节(64 位指针和双精度)。

Eigen/Core 头文件中 EIGEN_DONT_VECTORIZE 指令为 CUDA 启用。检查 documentation 时:

EIGEN_DONT_VECTORIZE - disables explicit vectorization when defined. Not defined by default, unless alignment is disabled by Eigen's platform test or the user defining EIGEN_DONT_ALIGN.

这基本上意味着特征矩阵在 CUDA 中没有特殊对齐方式,因此它们与元素类型对齐,double 在您的情况下,导致矩阵的 8 字节对齐,因此对于整个结构。

解决它的最好方法是强制对齐两种架构的结构。现在对 CUDA 不是很流利,我认为你可以在 CUDA 中使用 __align__(16)(更多 here), and using alignas(16) in C++ (since C++11)。如果您共享两种语言的声明,则可以定义一个宏来使用正确的运算符:

#ifdef __CUDACC__
# define MY_ALIGN(x) __align__(x)
#else
# define MY_ALIGN(x) alignas(x)
#endif

struct MY_ALIGN(16) S {
  // ...
};

无论如何,请注意此类低级副本,因为 Eigen 在 CUDA 中的实现可能与 C++ 中的不同(Eigen 的文档中对此没有保证)。