【问题标题】:CUDA Object copy from device to host从设备到主机的 CUDA 对象复制
【发布时间】:2023-03-14 19:53:01
【问题描述】:

我正在尝试将一个对象从设备复制回主机,它可以工作,但如果该对象包含指向某物的指针,我将找不到调用 cudaMemcpy 的正确方法。

这是一个简化的代码,用于显示我正在尝试做的事情。 cudaMemcpy 返回 cudaSuccess 但临时变量保持“空”。

class A {
public:
    int *s;
};

__global__ void MethodA(A *a) {
    printf("%d\n", a->s[2]);
}

int main() {
    A *a = new A();
    int asd[] = { 0, 1, 2, 3, 4 };
    a->s = asd;
    A *d_a;
    cudaMalloc((void**)&d_a, sizeof(A));
    cudaMemcpy(d_a, a, sizeof(A), cudaMemcpyHostToDevice);

    int * temp;
    cudaError e;
    e = cudaMalloc((void**)&temp, sizeof(int) * 5);
    e = cudaMemcpy(temp, a->s, sizeof(int) * 5, cudaMemcpyHostToDevice);
    e = cudaMemcpy(&(d_a->s), &temp, sizeof(int*), cudaMemcpyHostToDevice);

    MethodA << <1, 1 >> > (d_a);

    cudaMemcpy(a, d_a, sizeof(A), cudaMemcpyDeviceToHost);
    e = cudaMemcpy(&temp, a->s, sizeof(int) * 5, cudaMemcpyDeviceToHost);
    a->s = temp;

    cudaFree(d_a);
    delete(a);
    return 0;
}

【问题讨论】:

    标签: object cuda memcpy


    【解决方案1】:

    问题出在这里:

    e = cudaMemcpy(&(d_a->s), &temp, sizeof(int*), cudaMemcpyHostToDevice);
    

    d_a 是指向设备对象的指针,您不能在主机上取消引用它。 您必须先将s 复制到设备,然后在主机上创建一个A 类型的对象,该对象具有指向s 的设备副本的指针,然后将此对象复制到设备上。

    这是 CUDA 的一个已知问题,并且经常发生在链表或树等结构中,这也是 Nvidia 投入大量精力来改进 unified memory 的原因之一。如果您可以使用它,并且它不会降低您的应用程序的性能,那么它可以为您省去很多此类问题的麻烦。

    这是您解决问题的示例:

    class A {
    public:
        int *s;
    };
    
    __global__ void MethodA(A *a) {
        printf("%d\n", a->s[2]);
        a->s[2] = 6;
    }
    
    int main() {
        A *a = new A();
        int asd[] = { 0, 1, 2, 3, 4 };
        a->s = asd;
    
        A *a_with_d_s = new A();
        cudaMalloc(&(a_with_d_s->s), sizeof(int) * 5);
        cudaMemcpy(a_with_d_s->s, a->s, sizeof(int) * 5, cudaMemcpyHostToDevice);
    
        A *d_a;
        cudaMalloc(&d_a, sizeof(A));
        cudaMemcpy(d_a, a_with_d_s, sizeof(A), cudaMemcpyHostToDevice);
    
        MethodA << <1, 1 >> > (d_a);
    
        // note that if we call the following line, a->s will point to device
        // memory!
        //cudaMemcpy(a, d_a, sizeof(A), cudaMemcpyDeviceToHost);
        cudaMemcpy(a->s, a_with_d_s->s, sizeof(int) * 5, cudaMemcpyDeviceToHost);
    
        printf("%d\n", a->s[2]);
    
        cudaFree(d_a);
        cudaFree(a_with_d_s->s);
    
        delete(a);
        delete(a_with_d_s);
        return 0;
    }
    

    打印出来:

    2
    6
    

    【讨论】:

    • 如果我想将整个对象复制回来,这样做cudaMemcpy(a, d_a, sizeof(A), cudaMemcpyDeviceToHost); 不会像你说的那样工作。如果我将它复制到另一个变量而不是“合并”这两个变量,它会起作用,但有更好的方法吗?
    • 我也想添加到您的答案中,因为如果a 包含除s 之外的字段,它不会复制这些字段。将这些复制到a_with_d_s 即可解决问题。
    • @TóthBence 我不知道比你更好的方法 - 这里的问题是你需要对对象进行深层复制。即使您不必担心分布式内存方案,这也不是微不足道的。 C++ 通过不鼓励手动管理内存来解决这个问题,而是定义一组标准容器(如std::vectorstd::array)来代替使​​用。但是(AFAIK),标准 C++ 库在 CUDA 代码中不起作用。有thrust,你可以用它来代替,但我不是这方面的专家。
    • 谢谢,我已经阅读了一些关于推力的文章,但我认为它对复制没有帮助。
    猜你喜欢
    • 2013-09-20
    • 2012-09-29
    • 2020-08-20
    • 2017-08-27
    • 2013-03-25
    • 1970-01-01
    • 2013-04-10
    • 2013-08-06
    • 2016-02-09
    相关资源
    最近更新 更多