数组CUDA C的结构体数组

时间:2019-06-29 15:22:56

标签: cuda

我对CUDA还是很陌生,我一直在寻找创建数组结构和数组结构的方法,我找到了一些解决方案,但没有一个给我一个清晰的主意。

here Harrism解释了一个可以正常工作的结构的按值传递,但是当尝试向其添加this方法时,我得到了非法的内存访问。

我要实现的是一个结构数组,每个结构都带有指向主机和内核中动态分配的数组的指针,以便能够从AoS的所需索引中读取数组的值并将其用于内部的计算中内核。

我对这两个代码不了解什么,如何将这些想法结合在一起?
我尝试了什么(尝试2个结构体的数组,每个结构体1个数组):

#include <stdio.h>
#include <stdlib.h>
#define N 10
__inline __host__ void gpuAssert(cudaError_t code, char *file, int line, 
    bool abort=true)
{
if (code != cudaSuccess) 
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

typedef struct StructA {
    int* arr;
} StructA;

__global__ void kernel2(StructA *in)
{
    in[0].arr[threadIdx.x] = 0;
    in[1].arr[threadIdx.x] = 1;
    printf("d_arr = %d , d_arr2 = %d \n",in[0].arr[threadIdx.x],in[1].arr[threadIdx.x]);
}



int main(){
int* h_arr;
int* h_arr2;
h_arr = (int*)malloc(N*sizeof(int));
h_arr2 = (int*)malloc(N*sizeof(int));
StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);
int *d_arr;
int *d_arr2;
h_arr[0]=1;h_arr[1]=2;h_arr[2]=3,h_arr[3]=4,h_arr[4]=5;h_arr[5]=6;h_arr[6]=7;h_arr[7]=8;h_arr[8]=9;h_arr[9]=10;
h_arr2[0]=1;h_arr2[1]=2;h_arr2[2]=3,h_arr2[3]=4,h_arr2[4]=5;h_arr2[5]=6;h_arr2[6]=7;h_arr2[7]=8;h_arr2[8]=9;h_arr2[9]=10;
// 1. Allocate device array.
gpuErrchk(cudaMalloc((void**) &(d_arr), sizeof(int)*N));
gpuErrchk(cudaMalloc((void**) &(d_arr2), sizeof(int)*N));

// 2. Copy array contents from host to device.
gpuErrchk(cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice));
gpuErrchk(cudaMemcpy(d_arr2, h_arr2, sizeof(int)*N, cudaMemcpyHostToDevice));

// 3. Point to device pointer in host struct.
h_a[0].arr = d_arr;
h_a[1].arr = d_arr2;

// 4. Call kernel with host struct as argument
kernel2<<<1,N>>>(h_a);
gpuErrchk(cudaPeekAtLastError());
//gpuErrchk(cudaDeviceSynchronize());
// 5. Copy pointer from device to host.
gpuErrchk(cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost));

// 6. Point to host pointer in host struct 
//    (or do something else with it if this is not needed)
//h_a.arr = h_arr;
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr[0],h_arr[1],h_arr[2],h_arr[3],h_arr[4],h_arr[5],h_arr[6],h_arr[7],h_arr[8],h_arr[9]);
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr2[0],h_arr2[1],h_arr2[2],h_arr2[3],h_arr2[4],h_arr2[5],h_arr2[6],h_arr2[7],h_arr2[8],h_arr2[9]);
return 0;
}

2 个答案:

答案 0 :(得分:1)

您的代码大部分是正确的。

CUDA的基本原理是,您不能(不应)取消引用设备代码中的主机指针或引用宿主代码中的设备指针。

这是主机指针:

StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);

这会将其传递到设备代码(将在其中取消引用):

kernel2<<<1,N>>>(h_a);

我们可以通过一些其他代码来解决此问题,以将h_a指向的结构复制到d_a分配的一组新结构中的设备内存中,同时对内核调用进行相应的更改:< / p>

// 3a. Copy host structs to device
StructA *d_a;
cudaMalloc(&d_a, sizeof(StructA)*2);
cudaMemcpy(d_a, h_a, sizeof(StructA)*2, cudaMemcpyHostToDevice);


// 4. Call kernel with device struct as argument
kernel2<<<1,N>>>(d_a);

这是一个完整的例子:

$ cat t4.cu
#include <stdio.h>
#include <stdlib.h>
#define N 10
__inline __host__ void gpuAssert(cudaError_t code, const char *file, int line,
    bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

typedef struct StructA {
    int* arr;
} StructA;

__global__ void kernel2(StructA *in)
{
    in[0].arr[threadIdx.x] = 0;
    in[1].arr[threadIdx.x] = 1;
    printf("d_arr = %d , d_arr2 = %d \n",in[0].arr[threadIdx.x],in[1].arr[threadIdx.x]);
}



int main(){
int* h_arr;
int* h_arr2;
h_arr = (int*)malloc(N*sizeof(int));
h_arr2 = (int*)malloc(N*sizeof(int));
StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);
int *d_arr;
int *d_arr2;
h_arr[0]=1;h_arr[1]=2;h_arr[2]=3,h_arr[3]=4,h_arr[4]=5;h_arr[5]=6;h_arr[6]=7;h_arr[7]=8;h_arr[8]=9;h_arr[9]=10;
h_arr2[0]=1;h_arr2[1]=2;h_arr2[2]=3,h_arr2[3]=4,h_arr2[4]=5;h_arr2[5]=6;h_arr2[6]=7;h_arr2[7]=8;h_arr2[8]=9;h_arr2[9]=10;
// 1. Allocate device array.
gpuErrchk(cudaMalloc((void**) &(d_arr), sizeof(int)*N));
gpuErrchk(cudaMalloc((void**) &(d_arr2), sizeof(int)*N));

// 2. Copy array contents from host to device.
gpuErrchk(cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice));
gpuErrchk(cudaMemcpy(d_arr2, h_arr2, sizeof(int)*N, cudaMemcpyHostToDevice));

// 3. Point to device pointer in host struct.
h_a[0].arr = d_arr;
h_a[1].arr = d_arr2;

// 3a. Copy host structs to device
StructA *d_a;
cudaMalloc(&d_a, sizeof(StructA)*2);
cudaMemcpy(d_a, h_a, sizeof(StructA)*2, cudaMemcpyHostToDevice);


// 4. Call kernel with device struct as argument
kernel2<<<1,N>>>(d_a);
gpuErrchk(cudaPeekAtLastError());
//gpuErrchk(cudaDeviceSynchronize());
// 5. Copy pointer from device to host.
gpuErrchk(cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost));

// 6. Point to host pointer in host struct
//    (or do something else with it if this is not needed)
//h_a.arr = h_arr;
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr[0],h_arr[1],h_arr[2],h_arr[3],h_arr[4],h_arr[5],h_arr[6],h_arr[7],h_arr[8],h_arr[9]);
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr2[0],h_arr2[1],h_arr2[2],h_arr2[3],h_arr2[4],h_arr2[5],h_arr2[6],h_arr2[7],h_arr2[8],h_arr2[9]);
return 0;
}
$ nvcc -o t4 t4.cu
$ ./t4
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1

0 0 0 0 0 0 0 0 0 0

1 2 3 4 5 6 7 8 9 10
$

请注意,打印输出的最后几行未显示主机上更新的第二个数组,因为您尚未将该数组从设备内存复制回主机内存(内核之后只有一个cudaMemcpy语句码)。您可以使用另一个cudaMemcpy语句来解决此问题。我还为您的const添加了gpuAssert,以摆脱烦人的编译器警告提示。

answer可能为您提供有关如何处理指针数组的其他一些想法。

答案 1 :(得分:0)

在您的代码中,您正在将h_a传递给内核。 h_a是主机端C阵列。这些数组作为参数传递给函数时,会 decay 指向其第一个元素的指针;看到:

What is array decaying?

所以您的内核获取的是主机端StructA的地址-它不能使用它。您可以:

  • h_a复制到设备端(例如,复制到d_a中)并使用-衰减将是很好的,因为它是您要下标的设备端地址。
  • 使用固定大小的std::array,该大小不会衰减。
  • 分配h_a以便也可以从设备访问-使用cudaMallocManaged()。有关更多信息,请参见this presentation

已经说过-我觉得您根本不应该使用该数据结构。为什么要在外部数组的每个元素中进行如此多的指针解引用和不同,独立,任意的指针?这似乎效率很低。我会尝试以不同的方式排列我的数据。