访问不同CUDA内核中的类成员

时间:2017-12-15 16:03:01

标签: c++ cuda

我有一个GPU专用类T,我想在GPU上创建,但在CPU上有一个引用,所以我可以将链接作为参数发送到不同的CUDA内核。

class T
{
public:
    int v;
public:
    __device__ T() { v = 10; }
    __device__ ~T() {}
    __device__ int compute() { return v; }
};

以下是我创建类实例并调用compute()函数的内核。

__global__ void kernel(T* obj, int* out)
{
    if(blockIdx.x * blockDim.x + threadIdx.x == 0) {
        out[0] = obj->compute(); // no kernel error, but it returns garbage
    }
}

__global__ void cudaAllocateGPUObj(T* obj)
{
    if(blockIdx.x * blockDim.x + threadIdx.x == 0) {
        obj = new T;
        // if I call `out[0] = obj->compute();` here, everything works fine
    }
}

main函数只为类型为T*的指针分配内存,后者将用作cudaAllocateGPUObj的参数。

int main()
{
    int cpu, *gpu;
    cudaMalloc((void**)&gpu, sizeof(int));
    T* obj;
    cudaMalloc((void**)&obj, sizeof(T*));
    cudaAllocateGPUObj<<<1,1>>>(obj);
    kernel<<<1,1>>>(obj, gpu);
    cudaMemcpy(&cpu, gpu, sizeof(int), cudaMemcpyDeviceToHost);
    cudaDeviceSynchronize();
    printf("cudaMemcpy\nresult: %d\n", cpu);
    return 0;
}

此代码的问题(在代码中的注释中指定)是当我在out[0] = obj->compute();内核中调用cudaAllocateGPUObj并将获得的值传输到CPU时,一切都是正确的。但是如果我想在另一个内核中获取成员值,它会变成垃圾,但如果我将v变量的返回值更改为常量,一切正常。

请告诉我这段代码有什么问题。

1 个答案:

答案 0 :(得分:2)

将参数传递给CUDA内核时,它是一种按值传递的机制。您已经开始使用指向对象的指针:

T* obj;

然后,不是为对象分配存储,而是为另一个指针分配存储空间:

cudaMalloc((void**)&obj, sizeof(T*));

所以我们在这里走错了路。 (此时这是一个逻辑C编程错误。)接下来,在分配内核中,obj参数(现在指向GPU内存空间中的某个位置)按值传递

__global__ void cudaAllocateGPUObj(T* obj)
                                      ^^^ pass-by-value: local copy is made

现在,当你这样做时:

        obj = new T;

您创建新指针,并使用该新指针覆盖obj的本地副本。当然,这在本地工作,但调用环境中obj的副本不会使用新指针更新。

解决此问题的一种可能方法是创建适当的指针指针方法:

$ cat t5.cu
#include <stdio.h>

class T
{
public:
    int v;
public:
    __device__ T() { v = 10; }
    __device__ ~T() {}
    __device__ int compute() { return v; }
};

__global__ void kernel(T** obj, int* out)
{
    if(blockIdx.x * blockDim.x + threadIdx.x == 0) {
        out[0] = (*obj)->compute(); 
    }
}

__global__ void cudaAllocateGPUObj(T** obj)
{
    if(blockIdx.x * blockDim.x + threadIdx.x == 0) {
        *obj = new T;
    }
}

int main()
{
    int cpu, *gpu;
    cudaMalloc((void**)&gpu, sizeof(int));
    T** obj;
    cudaMalloc(&obj, sizeof(T*));
    cudaAllocateGPUObj<<<1,1>>>(obj);
    kernel<<<1,1>>>(obj, gpu);
    cudaMemcpy(&cpu, gpu, sizeof(int), cudaMemcpyDeviceToHost);
    cudaDeviceSynchronize();
    printf("cudaMemcpy\nresult: %d\n", cpu);
    return 0;
}

$ nvcc -arch=sm_35 -o t5 t5.cu
$ cuda-memcheck ./t5
========= CUDA-MEMCHECK
cudaMemcpy
result: 10
========= ERROR SUMMARY: 0 errors
$