我有一个自定义结构的 std::vector,其中包含两个 int (并且只有两个 int):
struct S {
int p0;
int p1;
};
std::vector<S> v(dimension);
我想要一个指向这个结构体数组的指针,将其解释为原始维度两倍的 int 数组。 上下文如下:struct 的 std::vector (此处为
v
)是在一些遗留代码中构造的。在我自己的代码中,这个数组必须与 GPU 通信(通过 cudaMemcpy)。
第一个解决方案是重新生成一个新的 int 数组,其维度是原始数组的两倍。然后新的 int 数组与 GPU 的通信就很简单了。
但是,我想避免在 CPU 内存上进行此复制,以节省时间和内存使用量。 相反,我希望安全地获得指向结构向量的第一个元素的指针。我可以确定这是安全的吗?我能否确保在阅读
p0
时,p1
、(p0, p1, p0, p1, ...)
字段在内存中与 v
对齐?换句话说,当 *(ptr+2*i)
指向时,我能否确定 *(ptr+2*i+1)
和 p0
将分别是原始数组 p1
的第 i 个元素的 v
和 ptr
整数到这个原始数组的顶端?
下面是一个简约的独立说明性示例。从这段代码来看,似乎一切都很顺利。会一直这样吗?
/*
Compilation: nvcc main.cu -o main.cuda
or g++ main.cpp -o main when removing cudaMemcpy's and cudaFree
*/
#include <vector>
#include <cassert>
#include <cuda_runtime.h>
struct S {
int p0;
int p1;
};
int main()
{
assert(sizeof(S)==2*sizeof(int));
unsigned int n = 10;
unsigned int dimension = (1UL)<<n; // =2^n
std::vector<S> v(dimension);
// initialize array of struct - done in legacy code
int shift = 10;
for (int i=0; i<dimension; ++i) {
v[i].p0 = shift+2*i;
v[i].p1 = shift+2*i+1;
}
int *d_v;
cudaMalloc((void**)&d_v, 2*dimension*sizeof(int));
// solution 1 - to be avoided - re-allocate the array on CPU memory and copy values to GPU
std::vector<int> v2(2*dimension);
for (int i=0; i<dimension; ++i) {
v2[2*i] = v[i].p0;
v2[2*i+1] = v[i].p1;
}
cudaMemcpy(d_v, v2.data(), 2*dimension, cudaMemcpyHostToDevice);
// solution 2 (?)
int * ptr = (int*)v.data(); // safe ?
// ptr should point to the "p0" field of the first element of v. Let's check:
for (int i=0; i<2*dimension; ++i) {
assert( *(ptr+i) == shift+i ); // Ok. Is it always true ?
}
cudaMemcpy(d_v, ptr, 2*dimension, cudaMemcpyHostToDevice); // Is it fully safe ?
/* ... use d_v in Cuda kernels */
cudaFree(d_v);
return 0;
}
你问:
换句话说,我能否确定
和*(ptr+2*i)
将分别是原始数组*(ptr+2*i+1)
的第i个元素的p0
和p1
整数,当v
时指向这个原始数组的尖端?ptr
严格来说,我无法在语言中找到任何可以保证这一点的内容。
如果您能够验证
sizeof(S)
等于 2*sizeof(int)
,我看不出有任何理由不成立。
即,在代码中插入以下行应该足以保证您的代码不会不当使用内存。
static_assert(sizeof(S) == 2*sizeof(int), "Objects are not aligned properly");