【问题标题】:Array of structs of arrays CUDA C数组结构的数组 CUDA C
【发布时间】:2019-06-30 00:08:15
【问题描述】:

我对 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;
}

【问题讨论】:

    标签: cuda


    【解决方案1】:

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

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

    这是一个主机指针:

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

    这是将它传递给设备代码(它将被取消引用):

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

    我们可以通过一些额外的代码来解决这个问题,将h_a 指向的结构复制到d_a 分配的一组新结构中的设备内存,并对内核调用进行相应的更改:

    // 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 语句来解决这个问题。我还在您的gpuAssert 中添加了const,以消除烦人的编译器警告。

    这个answer 可能会给你一些关于如何处理指针数组的其他想法。

    【讨论】:

    • 这正是我一直在尝试做的事情,我现在清楚地知道应该如何做。谢谢
    【解决方案2】:

    在您的代码中,您将h_a 传递给内核。 h_a 是主机端 C 数组。当作为参数传递给函数时,这些数组衰减为指向它们的第一个元素的指针;见:

    What is array decaying?

    所以你的内核得到的是主机端 StructA 的地址——它不能使用它。你可以:

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

    话虽如此 - 我觉得您根本不应该使用该数据结构。为什么要在外部数组的每个元素中进行如此多的指针取消引用,以及不同的、独立的、任意指针?这似乎相当低效。我会尝试以不同的方式排列我的数据。

    【讨论】:

    • h_a 不是主机端 C 数组。它是一个开始的指针,不会衰减。
    • 谢谢,我讨厌这种数据结构,用 C 语言编写也很痛苦。但我相信这是唯一的方法,因为在我想要实现此结构的目标代码中,从文件中获取半径(1-18),我需要与获取的半径匹配的数组,因此通过索引的结构数组。如果您还有其他想法,我愿意接受新方法。
    猜你喜欢
    • 2015-10-14
    • 1970-01-01
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    相关资源
    最近更新 更多