正确地将 int 结构数组重新解释为 int 数组

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

我有一个

std::vector
的自定义结构,其中包含两个整数(并且只有两个整数):

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;
}
c++ struct cuda pointer-aliasing
2个回答
2
投票

你问:

换句话说,我能否确定

*(ptr+2*i)
*(ptr+2*i+1)
将分别是原始数组
p0
的第i个元素的
p1
v
整数,当
ptr
时指向这个原始数组的尖端?

严格来说,我无法在语言中找到任何可以保证这一点的内容。

如果您能够验证

sizeof(S)
等于
2*sizeof(int)
,我看不出有任何理由不成立。

即,在代码中插入以下行应该足以保证您的代码不会不当使用内存。

static_assert(sizeof(S) == 2*sizeof(int), "Objects are not aligned properly");

另外说明,我会改变

int* ptr = (int*)v.data();

int* ptr = &(v.data()[0].p0);

0
投票

另一种方法可以让您不必通过语言律师来解决正式和实际的错误,可能如下:

  • 分配原始整数,例如使用
    std::make_unique<int[]>(2*dimension)
  • 当您想要引用原始
    std::span<int>
    的整个双倍长度时,请使用
    int
  • 仅将跨度传递给需要使用它的函数(而不是调整它的大小)。
  • 当您需要
    struct S
    时,可以根据跨度中的一对连续整数当场构造它。该构造可能会被编译器优化,即,如果您有
    foo(const S my_s)
    ,并且调用
    foo(S{my_ints[456], my_ints[457]})
    - 很可能不需要发生实际构造,并且这些 int 将仅被放置在 CPU 寄存器中那里用过。
© www.soinside.com 2019 - 2024. All rights reserved.