使用内核时,我遇到了一些问题,该内核使用了我在c ++中定义的某些结构。错误cuda-memcheck给我的提示是对齐问题。
我要使用的结构包含一些指针,我认为这给了我一些问题。我已经在.cu文件和内核中的主机函数中打印了C ++端和CUDA端的结构大小控制台。这给出了不同的结果,解释了我所遇到的问题,但是我不确定为什么会发生,也不确定如何解决。
我正在使用的结构如下
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::node0
,S::node1
或S::node3
时,我收到一个未对齐的访问错误。
所以我对此问题有三个疑问:
编辑:
多亏了被接受的答案,我才能够了解出现此问题的原因。 Eigen在可能的情况下使用矢量化,并为此请求16字节对齐。当本征对象大小是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个填充字节。
答案 0 :(得分:1)
这种差异是由于Eigen在C ++和CUDA中请求内存对齐的方式。
在C ++中,S
被对齐为16个字节(您可以检查alignof(S) == 16
)。这是由于Eigen的矩阵对齐到16个字节,可能是由于使用了需要这种对齐的SSE寄存器。您其余的字段对齐为8字节(64位指针和双精度)。
在Eigen/Core
头文件EIGEN_DONT_VECTORIZE
指令中为CUDA启用了。检查documentation时:
EIGEN_DONT_VECTORIZE-定义后禁用显式矢量化。默认情况下未定义,除非通过Eigen的平台测试或用户定义EIGEN_DONT_ALIGN禁用对齐。
基本上意味着Eigen矩阵在CUDA中没有特殊的对齐方式,因此它们与元素类型double
对齐(在您的情况下),从而导致矩阵以及整个结构的8字节对齐。
解决此问题的最佳方法是强制两种结构的结构对齐。目前在CUDA中不太熟练,我认为您可以在CUDA中使用__align__(16)
(更多here),并在C ++中使用alignas(16)
(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的文档中对此没有保证)。