这种分配设备对象的方式是“正确的”吗?

时间:2013-04-19 19:13:14

标签: cuda

关于如何直接在设备上分配对象而不是“I asked a question”之前

SO normal

  1. 在主机上分配
  2. 复制到设备
  3. 将动态分配的字段逐个复制到设备
  4. 我希望它直接在设备上分配的主要原因是我不想手动逐个复制每个动态分配的字段。

    无论如何,所以我认为我实际上找到了一种方法来做到这一点,我希望看到来自更有经验的CUDA程序员(如Robert Crovella)的一些意见。

    让我们先看看代码:

    class Particle
    {
        public:
        int *data;
    
        __device__ Particle()
        {
            data = new int[10];
            for (int i=0; i<10; i++)
            {
                data[i] = i*2;
            }
        }
    };
    
    
    __global__ void test(Particle **result)
    {
        Particle *p = new Particle();
    
        result[0] = p; // store memory location
    }
    
    __global__ void test2(Particle *p)
    {
        for (int i=0; i<10; i++)
            printf("%d\n", p->data[i]);
    
    }
    
    int main() {
        // initialise and allocate an object on device
        Particle **d_p_addr;
        cudaMalloc((void**)&d_p_addr, sizeof(Particle*));
        test<<<1,1>>>(d_p_addr);
    
        // copy pointer to host memory
        Particle  **p_addr = new Particle*[1];
        cudaMemcpy(p_addr, d_p_addr, sizeof(Particle*), cudaMemcpyDeviceToHost);
    
        // test:
        test2<<<1,1>>>(p_addr[0]);
    
        cudaDeviceSynchronize();
    
        printf("Done!\n");
    
    }
    

    如你所见,我所做的是:

    1. 调用内核,该内核初始化设备上的对象并将其指针存储为输出参数
    2. 将指针从设备内存复制到主机内存
    3. 现在你可以将指针传递给另一个内核!
    4. 这段代码确实有用,但我不确定是否有缺点。

      干杯

      编辑:正如罗伯特所指出的那样,没有必要先在主机上创建一个指针,所以我从代码中删除了那部分。

1 个答案:

答案 0 :(得分:3)

是的,你可以这样做。

您正在设备上分配一个对象,并将指针从一个内核传递给下一个内核。由于device malloc/new的特性是分配在上下文的生命周期(不仅仅是内核)中持续存在,因此分配不会在内核结束时消失。这基本上是标准的C ++行为,但我认为值得重复。因此,从一个内核传递到下一个内核的指针在程序上下文中的任何后续设备代码中有效。

然而,您可能想要了解一些皱纹。通过设备上完成的动态分配返回的指针(例如通过设备代码中的newmalloc 可用于将数据从设备传输到主机,至少在目前cuda的化身(cuda 5.0及更早版本)。造成这种情况的原因有点神秘(翻译:我无法充分解释)但考虑动态分配来自设备堆这一事实是有益的,这是一个逻辑上与运行时全局内存区域分离的区域cudaMalloccudaMemcpy等API函数使用。给出了here:

的斜向指示
  

为设备堆保留的内存是通过主机端CUDA API调用(如cudaMalloc())分配的内存的补充。

如果你想向自己证明这个问题,请在第二次内核调用后尝试添加以下看似无害的代码:

Particle *q;
q = (Particle *)malloc(sizeof(Particle));
cudaMemcpy(q, p_addr[0], sizeof(Particle), cudaMemcpyDeviceToHost);

如果您然后检查从该cudaMemcpy操作返回的API错误值,您将发现错误。

作为一个不相关的评论,你在我的书中使用指针*p有点怪异,并且给出的关于它的编译器警告表明了它的奇怪性。这在技术上并不违法,因为你实际上并没有对那个指针做任何有意义的事情(你会立即在你的内核1中替换它),但是它很奇怪,因为你正在向你没有正确cudaMalloc的内核传递一个指针。在你所展示的内容中,它是完全没有必要的,你可以消除内核1的第一个参数并用局部变量替换,消除了wierdness和编译器警告。