指向CUDA中对象的指针数组的指针

时间:2016-08-16 15:00:31

标签: c++ c arrays pointers cuda

我已经按照this questionthis link提供的指导来处理将指针数组传递给设备并返回的概念,但是当指针指向我时,我似乎正在努力解决我的具体情况到一个对象。请参阅下面的示例代码,我已经删除了错误检查以便简洁。

// Kernel
__global__ void myKernel(Obj** d_array_of_objs)
{
    // Change the scalar of each object to 5
    // by dereferencing device array to get 
    // appropriate object pointer.
    *d_array_of_objs->changeToFive();    <--------- SEE QUESTION 4
}

// Entry point
int main()
{

    /********************************/
    /* INITIALISE OBJ ARRAY ON HOST */
    /********************************/

    // Array of 3 pointers to Objs
    Obj* h_obj[3];
    for (int i = 0; i < 3; i++) {
        h_obj[i] = new Obj();       // Create
        h_obj[i]->scalar = i * 10;  // Initialise
    }

    // Write out
    for (int i = 0; i < 3; i++) {
        std::cout << h_obj[i]->scalar << std::endl;
    }


    /**************************************************/
    /* CREATE DEVICE VERSIONS AND STORE IN HOST ARRAY */
    /**************************************************/

    // Create host pointer to array-like storage of device pointers
    Obj** h_d_obj = (Obj**)malloc(sizeof(Obj*) * 3);    <--------- SEE QUESTION 1
    for (int i = 0; i < 3; i++) {
        // Allocate space for an Obj and assign
        cudaMalloc((void**)&h_d_obj[i], sizeof(Obj));
        // Copy the object to the device (only has single scalar field to keep it simple)
        cudaMemcpy(h_d_obj[i], &(h_obj[i]), sizeof(Obj), cudaMemcpyHostToDevice);
    }

    /**************************************************/
    /* CREATE DEVICE ARRAY TO PASS POINTERS TO KERNEL */
    /**************************************************/

    // Create a pointer which will point to device memory
    Obj** d_d_obj = nullptr;
    // Allocate space for 3 pointers on device at above location
    cudaMalloc((void**)&d_d_obj, sizeof(Obj*) * 3);
    // Copy the pointers from the host memory to the device array
    cudaMemcpy(d_d_obj, h_d_obj, sizeof(Obj*) * 3, cudaMemcpyHostToDevice);


    /**********
     * After the above, VS2013 shows the memory pointed to by d_d_obj 
     * to be NULL <------- SEE QUESTION 2.
     **********/


    // Launch Kernel
    myKernel <<<1, 3>>>(d_d_obj);

    // Synchronise and pass back to host
    cudaDeviceSynchronize();
    for (int i = 0; i < 3; i++) {
        cudaMemcpy(&(h_obj[i]), h_d_obj[i], sizeof(Obj), cudaMemcpyDeviceToHost);     <--------- SEE QUESTION 3
    }

    // Write out
    for (int i = 0; i < 3; i++) {
        std::cout << h_obj[i]->scalar << std::endl;
    }

    return 0;
}

所以问题是:

  1. 如果上面SEE QUESTION 1指示的行为指针分配主机内存,并且在后续循环中使用cudaMalloc来分配设备内存,则h_d_obj指向的指针得到用设备地址覆盖,这是否意味着我已为3 Obj*分配了主机内存,现在没有指向它的指针?

  2. 为什么在我测试返回的状态时cudaMemcpy成功但显然没有正确复制地址?我期望h_d_objd_d_obj的内存地址的“数组”是相同的,因为它们应指向设备地址空间中的相同Obj

    < / LI>
  3. 在第SEE QUESTION 3行,假设我在问题2中是正确的。我还希望能够使用h_d_objd_d_obj来检索{{1}来自设备的对象,因为区别仅在于我是否取消引用主机指针以访问指向Obj的设备指针或设备指针,我可以用Obj方法执行这两种操作吗?如果我使用写入的内容,则复制成功,但cudaMemcpy处的指针已损坏,我无法写出数据。

  4. 在第h_obj[0]行,为什么我不能取消引用SEE QUESTION 4来获取Obj**,然后使用Obj*运算符来调用设备方法?编译器怀疑它不是指向类类型的指针,它是->的事实告诉我它是。

1 个答案:

答案 0 :(得分:2)

首先,如果您提供完整的代码,包括Obj类的定义,这将非常方便。我根据您的代码检查和一些猜测提供了一个。

其次,你在这里的大部分困惑似乎都是一个不那么清晰的设施,用C(或C ++)指针。在主机和设备之间使用带有双指针结构(**)的CUDA API需要清晰的理解和能够可视化正在发生的事情。

  

如果上面的SEE QUESTION 1指示的行为指针分配主机内存,并且在后续循环中使用cudaMalloc分配设备内存后,h_d_obj指向的指针将被覆盖设备地址,这是否意味着我已经为3 Obj *分配了主机内存,现在没有指向它的指针?

没有。 h_d_obj操作建立malloc(即给出有意义的值)。之后您没有做任何事情来修改h_d_obj的值。

  

为什么在测试返回的状态时cudaMemcpy是否成功但显然没有正确复制地址?我期待&#34;阵列&#34; h_d_objd_d_obj的内存地址相同,因为它们应指向设备地址空间中的相同Obj。

到目前为止,我还没有发现您的代码有任何问题。 h_d_obj的值(先前)由malloc建立,其数值是主机内存中的地址。 d_d_obj的值由cudaMalloc建立,其数值是设备内存中的地址。在数字上,我希望它们是不同的。

  

在SEE QUESTION 3行,假设我在问题2中更正。我也希望能够使用h_d_objd_d_obj从设备中检索Obj对象。不同之处仅在于我是否取消引用主机指针来访问Obj的设备指针或设备指针,我可以用cudaMemcpy方法做到这两点吗?如果我使用写入的内容,则复制成功,但h_obj [0]处的指针已损坏,我无法写出数据。

NO。如果它是cudaMemcpy中的参数,则无法取消引用主机代码中的设备指针,甚至。这在cudaMemcpy操作中作为来源或目的地是合法的:

h_d_obj[i]

这不合法:

d_d_obj[i]

原因是为了获得实际的目标地址,我必须在第一种情况下取​​消引用主机指针(即访问主机上的存储器位置),而在第二种情况下取​​消引用设备指针。从主机代码,我可以检索h_d_obj[i]的内容。我不允许尝试在主机代码中检索d_d_obj[i]的内容(cudaMemcpy的参数操作是主机代码)。 d_d_obj的值可以用作主机代码的目标。 d_d_obj[i]不能。

  

在SEE QUESTION 4行,为什么我不能取消引用Obj **以获得Obj *然后使用 - &gt;运营商调用设备方法?编译器怀疑它不是指向类类型的指针,它是Obj *的事实告诉我它是。

由于您不了解您正在使用的各种运算符(*->)之间的运算顺序,编译器正在咆哮您。如果添加括号以标识正确的顺序:

(*d_array_of_objs)->changeToFive(); 

然后编译器不会反对(尽管我会做的略有不同,如下所示)。

这里是代码的修改版本,添加了Obj定义,对内核稍作更改,以便独立线程可以处理独立对象,以及其他一些修复。你的代码大多是正确的:

$ cat t1231.cu
#include <iostream>

class Obj{

  public:
  int scalar;
  __host__ __device__
  void changeToFive() {scalar = 5;}
};

// Kernel
__global__ void myKernel(Obj** d_array_of_objs)
{
    // Change the scalar of each object to 5
    // by dereferencing device array to get
    // appropriate object pointer.
    int idx = threadIdx.x+blockDim.x*blockIdx.x;
    // (*d_array_of_objs)->changeToFive();  //  <--------- SEE QUESTION 4 (add parenthesis)
    d_array_of_objs[idx]->changeToFive();
}

// Entry point
int main()
{

    /********************************/
    /* INITIALISE OBJ ARRAY ON HOST */
    /********************************/

    // Array of 3 pointers to Objs
    Obj* h_obj[3];
    for (int i = 0; i < 3; i++) {
        h_obj[i] = new Obj();       // Create
        h_obj[i]->scalar = i * 10;  // Initialise
    }

    // Write out
    for (int i = 0; i < 3; i++) {
        std::cout << h_obj[i]->scalar << std::endl;
    }


    /**************************************************/
    /* CREATE DEVICE VERSIONS AND STORE IN HOST ARRAY */
    /**************************************************/

    // Create host pointer to array-like storage of device pointers
    Obj** h_d_obj = (Obj**)malloc(sizeof(Obj*) * 3); //    <--------- SEE QUESTION 1
    for (int i = 0; i < 3; i++) {
        // Allocate space for an Obj and assign
        cudaMalloc((void**)&h_d_obj[i], sizeof(Obj));
        // Copy the object to the device (only has single scalar field to keep it simple)
        cudaMemcpy(h_d_obj[i], &(h_obj[i]), sizeof(Obj), cudaMemcpyHostToDevice);
    }

    /**************************************************/
    /* CREATE DEVICE ARRAY TO PASS POINTERS TO KERNEL */
    /**************************************************/

    // Create a pointer which will point to device memory
    Obj** d_d_obj = NULL;
    // Allocate space for 3 pointers on device at above location
    cudaMalloc((void**)&d_d_obj, sizeof(Obj*) * 3);
    // Copy the pointers from the host memory to the device array
    cudaMemcpy(d_d_obj, h_d_obj, sizeof(Obj*) * 3, cudaMemcpyHostToDevice);


    /**********
     * After the above, VS2013 shows the memory pointed to by d_d_obj
     * to be NULL <------- SEE QUESTION 2.
     **********/


    // Launch Kernel
    myKernel <<<1, 3>>>(d_d_obj);

    // Synchronise and pass back to host
    cudaDeviceSynchronize();
    for (int i = 0; i < 3; i++) {
        cudaMemcpy(h_obj[i], h_d_obj[i], sizeof(Obj), cudaMemcpyDeviceToHost);  //   <--------- SEE QUESTION 3  remove parenthesis
    }

    // Write out
    for (int i = 0; i < 3; i++) {
        std::cout << h_obj[i]->scalar << std::endl;
    }

    return 0;
}
$ nvcc -o t1231 t1231.cu
$ cuda-memcheck ./t1231
========= CUDA-MEMCHECK
0
10
20
5
5
5
========= ERROR SUMMARY: 0 errors
$

h_d_objd_d_obj的图表可能有所帮助:

HOST                               |    DEVICE
h_d_obj-->(Obj *)-------------------------->Obj0<---(Obj *)<----|
          (Obj *)-------------------------->Obj1<---(Obj *)     |
          (Obj *)-------------------------->Obj2<---(Obj *)     |
                                   |                            |
d_d_obj---------------------------------------------------------|
HOST                               |    DEVICE

您可以访问上图的左侧(主机),主机代码或cudaMemcpy操作中的任何数量(位置)。您不允许在主机代码中访问右侧的任何数量(位置)。