c++ - 指向 CUDA 中对象指针数组的指针

标签 c++ c arrays pointers cuda

我已遵循 this question 提供的指导和 this 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设备地址空间。

  3. SEE QUESTION 3 行,假设我在问题 2 中是正确的。我还希望能够使用 h_d_obj d_d_obj 从设备中检索 Obj 对象,因为区别仅在于我是取消引用主机指针以访问指向 Obj 的设备指针还是设备指针这两个我都可以在 cudaMemcpy 方法中完成,对吗?如果我使用写入的内容,复制成功,但 h_obj[0] 处的指针已损坏,我无法写出数据。

  4. SEE QUESTION 4 行,为什么我不能取消引用 Obj** 来获取 Obj* 然后使用-> 运算符调用 device 方法?编译器提示说它不是指向类类型的指针,事实上它是一个 Obj* 告诉我它是。

最佳答案

首先,如果您提供完整的代码(包括Obj 类的定义)会很方便。我根据对您的代码的检查和一些猜测提供了一个。

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

If the line indicated by SEE QUESTION 1 above allocates host memory for the pointers, and once I have used cudaMalloc in the subsequent loop to allocate device memory, the pointer pointed to by h_d_obj get overwritten with device addresses, does that mean I have allocated host memory for 3 Obj* that now has no pointer pointing to it?

没有。 h_d_objmalloc 操作建立(即赋予有意义的值)。您之后所做的任何事情都不会修改 h_d_obj 的值。

Why is the cudaMemcpy succeeding when I test the status returned but clearly does not copy the addresses correctly? I was expecting the "arrays" of memory address of both h_d_obj and d_d_obj to be the same since they should point to the same Obj in the device address space.

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

At the line SEE QUESTION 3, assuming I'm correct in question 2. I also expect to be able to use either h_d_obj or d_d_obj to retrieve the Obj objects from the device since the difference would be only whether I dereference a host pointer to access a device pointer to Obj or a device pointer both of which I can do in a cudaMemcpy method right? If I use what is written, the copy succeeds but the pointer at h_obj[0] is corrupted and I cannot write out the data.

没有。如果它是 cudaMemcpy 中的参数,则您不能取消引用主机代码中的设备指针,即使。这作为 cudaMemcpy 操作中的源或目标是合法的:

h_d_obj[i]

这是不合法的:

d_d_obj[i]

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

At the line SEE QUESTION 4, why can I not dereference an Obj** to get a Obj* then use the -> operator to call a device method? The compiler moans that it is not a pointer to class type which the fact that it is a Obj* tells me it is.

编译器对你咆哮是因为你不明白你正在使用的各种运算符(*->)之间的操作顺序。如果您添加括号来标识正确的顺序:

(*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 操作中访问上图左侧 (HOST) 的任何数量(位置)。您不能访问主机代码右侧的任何数量(位置)。

关于c++ - 指向 CUDA 中对象指针数组的指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38978297/

相关文章:

c++ - 为什么每当我尝试运行此 LinkedList 删除函数时都会收到段错误错误?

c++ - Facebook folly::AccessSpreader 是如何工作的?

如果我使用 C 代码,Android Studio 无法调试

c - 分配错误 : incorrect checksum for freed object

c++ - u8-literals 应该如何工作?

c++ - 为什么 RegOpenKeyEx() 在 Vista 64 位上返回错误代码 2?

c - 如何使用 clang 在 OS X 上使用 c11 标准库进行编译?

java - 使用 for 循环检查数组

c - 不允许在 C51 中定义包含数组的结构?

javascript - 更改数组的顺序以匹配一组 DOM 元素的顺序