【问题标题】:How to pass a C++ class with array of pointers to CUDA?如何将带有指针数组的 C++ 类传递给 CUDA?
【发布时间】:2013-01-25 07:21:43
【问题描述】:

更清楚地说,我想要的是传递指针和它们指向设备的所有数据。为了测试如何实现这个目标,我编写了一个简单的类:

class vecarray{
    public:
        int * vecptr[N];                //array of pointers pointing to array
        int dim[N];                     //store length of each array pointed to
        __device__ __host__ vecarray(); //constructor
        __device__ __host__ int sum();  //sum up all the elements in the array being              
                                       //pointed to
}

vecarray::vecarray(){
    for(int i = 0; i<N; i++)
    {
        vecptr[i] = NULL;
        dim[i] = 0;
    }
}

int vecarray::sum(){
    int i=0, j=0, s=0;
    for (i=0; i<N; i++)
        for(j=0; j < dim[i]; j++)
            s += vecptr[i][j];
    return s;
}

然后我在下面的代码中使用这个类:

#define N 2
__global__ void addvecarray( vecarray * v, int *s){
    *s = v->sum();
}

int main(){                                 //copy *V to device, do sum() and pass back 
    vecarray *v, *dev_v;                    //the result by dev_v
    v = new vecarray;
    dev_v = new vecarray;
    int a[3] = {1,2,3};                     //initialize v manually
    int b[4] = {4,5,6,7};
    int result = 0;
    int * dev_result;
    v->vecptr[0] = a;
    v->vecptr[1] = b;
    v->dim[0] = 3; v->dim[1] = 4;


    cudaMalloc((void**)&dev_v, sizeof(vecarray));      

    cudaMemcpy(dev_v, v, sizeof(vecarray),cudaMemcpyHostToDevice); //copy class object 

    for(int i = 0; i < N; i++){
        cudaMalloc((void**)&(dev_v->vecptr[i]), v->dim[i]*sizeof(int));
    }

    for(int i = 0; i<N; i++ ){                   //copy arrays
    cudaMemcpy(dev_v->vecptr[i], v->vecptr[i], v->dim[i]*sizeof(int), cudaMemcpyHostToDevice));
    }
    addvecarray<<<1,1>>>(dev_v, dev_result);

    cudaMemcpy(&result, dev_result, sizeof(int), cudaMemcpyDeviceToHost);
    printf("the result is %d\n", result);
}

代码通过 nvcc 编译器,但运行时因分段错误而失败。我检查了问题在于 for 循环中的两个 cudaMalloc 和 cudaMemcpy 操作。所以我的问题是我应该如何将这个对象传递给 CUDA?提前致谢。

【问题讨论】:

  • 我相信这个问题与this one 重复。在您有一个执行 cudaMalloc 操作的 for 循环的代码行中,您将作为指向 cudaMalloc 的指针传递一个已经存在于设备内存中的指针。相反,您需要在主机上创建一组单独的 int 指针,cudaMalloc 这些,然后将它们 cudaMemcpy 到设备中的 vecarray 对象中的适当位置,在 dev_v 实例化。

标签: c++ class pointers cuda


【解决方案1】:

您的代码中有几个错误。正如我在 cmets 中提到的,关键错误之一是如何为类中指针引用的数据区域分配内存。那里的关键错误是您正在传递一个指向已经存在于设备内存中的 cudaMalloc 的指针。我们可以通过创建一组额外的指针来解决这个问题,我们将使用这些指针为类中指向的数组分配所需的设备存储空间。此外还有一些其他错误,例如您没有为dev_result 正确分配设备存储。以下代码修复了我能找到的所有错误,我相信会给出正确的结果。我还添加了一个 cuda 错误检查的参考形式,您可能会发现它对您的项目有用:

#include <stdio.h>

#define N 2
#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

using namespace std;

class vecarray{
    public:
        int *vecptr[N];                //array of pointers pointing to array
        int dim[N];                     //store length of each array pointed to

        __device__ __host__ vecarray(); //constructor
        __device__ __host__ int sum();  //sum up all the elements in the array being
                                       //pointed to
};

vecarray::vecarray(){
    for(int i = 0; i<N; i++)
    {
        vecptr[i] = NULL;
        dim[i] = 0;
    }
}

__device__ __host__ int vecarray::sum(){
    int i=0, j=0, s=0;
    for (i=0; i<N; i++)
        for(j=0; j < dim[i]; j++)
            s += vecptr[i][j];
    return s;
}

__global__ void addvecarray( vecarray * v, int *s){
    *s = v->sum();
}

int main(){                                 //copy *V to device, do sum() and pass back
    vecarray *v, *dev_v;                    //the result by dev_v
    v = new vecarray;
    int a[3] = {1,2,3};                     //initialize v manually
    int b[4] = {4,5,6,7};
    int result = 0;
    int *dev_result;
    v->vecptr[0] = a;
    v->vecptr[1] = b;
    v->dim[0] = 3; v->dim[1] = 4;
    int *vptr[N];

    cudaMalloc((void**)&dev_v, sizeof(vecarray));
    cudaCheckErrors("cudaMalloc1 fail");
    cudaMemcpy(dev_v, v, sizeof(vecarray),cudaMemcpyHostToDevice); //copy class object
    cudaCheckErrors("cudaMemcpy1 fail");

    for(int i = 0; i < N; i++){
        cudaMalloc((void**)&(vptr[i]), v->dim[i]*sizeof(int));
        cudaCheckErrors("cudaMalloc2 fail");
        cudaMemcpy(&(dev_v->vecptr[i]), &vptr[i], sizeof(int*), cudaMemcpyHostToDevice);
        cudaCheckErrors("cudaMemcpy2 fail");
    }

    for(int i = 0; i<N; i++ ){                   //copy arrays
        cudaMemcpy(vptr[i], v->vecptr[i], v->dim[i]*sizeof(int), cudaMemcpyHostToDevice);
        cudaCheckErrors("cudaMemcpy3 fail");
    }
    cudaMalloc((void **)&dev_result, sizeof(int));
    cudaCheckErrors("cudaMalloc3 fail");
    addvecarray<<<1,1>>>(dev_v, dev_result);

    cudaMemcpy(&result, dev_result, sizeof(int), cudaMemcpyDeviceToHost);
    cudaCheckErrors("cudaMemcpy4 fail");
    printf("the result is %d\n", result);
    return 0;
}

【讨论】:

  • 非常感谢。该代码有效。我注意到在你上面提到的重复问题的线程中,Eric 在第一条评论中提到这种错误的原因是我们试图取消引用指向主机上设备地址的指针,这也是我听到的在其他一些地方。但是在上面代码的第一个 for 循环中,cudaMemcpy 的第一个参数 &(dev_v->vecptr[i]) 也首先在括号内执行取消引用操作,因为它等于 &((*dev).vecptr [一世])。如果是这样,这里的语句和代码是不是有冲突?
  • 另外,请问为什么我们不能将已经指向地址的指针作为指向 cudaMalloc() 的指针传递?我想到的是这是为了防止内存泄漏。因为如果我们将指向 cudaMalloc() 返回的新地址的指针移开,我们就会忘记驻留在同一个指针指向的先前地址中的变量,并且我们不能再访问该内存。那是对的吗?再次感谢。
  • 哦,请忽略上面的第二个问题。正如你在另一个线程中所说的那样,我刚刚弄清楚了原因。很抱歉。
  • 对于您的取消引用问题,cudaMemcpy 是一种特殊情况,因为我们可以将设备指针作为目标传递给复制操作,这将作为复制过程的一部分被取消引用。普通主机代码不应该取消引用设备指针,但 cudaMemcpy 是一个特例。否则,如果我们不能以这种方式取消引用设备指针,我们怎么能从主机复制数据到它呢?但是 cudaMalloc 不允许这样做。传递给 cudaMalloc 的指针的地址应该驻留在主机上。
  • @RobertCrovella 关于int *vptr[N]; 的一个问题,如果 N 是变量,如何做同样的事情?(使数组大小动态化)
猜你喜欢
  • 2012-07-19
  • 2020-01-01
  • 2012-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 2011-04-19
  • 1970-01-01
相关资源
最近更新 更多