C++ 有没有办法将vtable从主机复制到设备(CUDA&;C+;+;)

C++ 有没有办法将vtable从主机复制到设备(CUDA&;C+;+;),c++,cuda,C++,Cuda,由于与“虚拟表”或“虚拟指针”相关的某些原因,Cuda似乎不允许我“将从虚拟基类派生的类的对象传递给\uuuu全局\uuuu函数” 我想知道是否有办法手动设置“虚拟指针”,以便使用多态性 有没有办法将vtable从主机复制到设备 您不希望将vtable从主机复制到设备。主机上的vtable(即在主机上创建的对象中)在vtable中有一组主机函数指针。当您将这样一个对象复制到设备上时,vtable不会被更改或“修复”,因此您最终会在设备上得到一个对象,该对象的vtable充满了主机指针 如果然后尝

由于与“虚拟表”或“虚拟指针”相关的某些原因,Cuda似乎不允许我“将从虚拟基类派生的类的对象传递给
\uuuu全局\uuuu
函数”

我想知道是否有办法手动设置“虚拟指针”,以便使用多态性

有没有办法将vtable从主机复制到设备

您不希望将vtable从主机复制到设备。主机上的vtable(即在主机上创建的对象中)在vtable中有一组主机函数指针。当您将这样一个对象复制到设备上时,vtable不会被更改或“修复”,因此您最终会在设备上得到一个对象,该对象的vtable充满了主机指针

如果然后尝试调用其中一个虚拟函数(使用设备上的对象,从设备代码),就会发生不好的事情。vtable中列出的数字函数入口点是在设备代码中没有任何意义的地址

这样我就可以使用多态性了

我建议在设备代码中使用多态性的方法是在设备上创建对象。这将使用一组设备函数指针(而不是主机函数指针)设置vtable,并通过一些问题(如)演示vtable是否工作。按照一阶近似,如果你有办法在主机代码中创建一组多态对象,我不知道为什么你不能在设备代码中使用类似的方法。这个问题实际上与互操作性有关——在主机和设备之间移动这样的对象——这就是我们所指的

我想知道有没有办法手动设置“虚拟指针”

可能有。为了分享知识,我将概述一种方法。但是,我不太清楚C++是否可以接受。我唯一能说的是,在我非常有限的测试中,它似乎起了作用但我认为这是不合法的,因此我不建议您将此方法用于实验以外的任何事情。即使我们不确定它是否合法,但已经有一个声明的CUDA限制(如上所述),即您不应尝试在主机和设备之间传递具有虚拟功能的对象。所以我只是把它作为一种观察,这可能对实验或研究很有意思。我不建议将其用于生产代码

中概述了基本思想。这是基于这样一个想法,即普通对象副本似乎不会复制虚拟函数指针表,这对我来说是有意义的,但对象作为一个整体确实包含该表。因此,如果我们使用这样的方法:

template<typename T>
__device__ void fixVirtualPointers(T *other) {
        T temp =  T(*other); // object-copy moves the "guts" of the object w/o changing vtable
        memcpy(other, &temp, sizeof(T)); // pointer copy seems to move vtable
}
模板
__设备\无效固定虚拟指针(T*其他){
T temp=T(*其他);//对象复制在不改变vtable的情况下移动对象的“内脏”
memcpy(other,&temp,sizeof(T));//指针副本似乎在移动vtable
}
似乎可以获取给定对象,创建该类型的新“虚拟”对象,然后通过对该对象进行基于指针的复制(考虑整个对象大小)而不是“典型”对象复制来“修复”vtable。使用此软件的风险自负。读起来可能也很有趣,尽管我不能保证那里的任何陈述都是正确的


除此之外,在
cuda
标签上还有许多其他建议,您可能希望查看。

我想提供一种不同的方法来修复vtable,它不依赖于在对象之间复制vtable。想法是在设备上使用placement new,让编译器生成适当的vtable。但是,这种方法也违反了编程指南中规定的限制

#include <cstdio>

struct A{
    __host__ __device__
    virtual void foo(){
        printf("A\n");
    }
};

struct B : public A{

    B(int i = 13) : data(i){}

    __host__ __device__
    virtual void foo() override{
        printf("B %d\n", data);
    }

    int data;
};

template<class T>
__global__
void fixKernel(T* ptr){
    T tmp(*ptr);

    new (ptr) T(tmp);
}

__global__
void useKernel(A* ptr){
    ptr->foo();
}


int main(){

    A a;
    a.foo();

    B b(7); 
    b.foo();

    A* ab = new B();

    ab->foo();

    A* d_a;
    cudaMalloc(&d_a, sizeof(A));
    cudaMemcpy(d_a, &a, sizeof(A), cudaMemcpyHostToDevice);

    B* d_b;
    cudaMalloc(&d_b, sizeof(B));
    cudaMemcpy(d_b, &b, sizeof(B), cudaMemcpyHostToDevice);

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

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

    fixKernel<<<1,1>>>(d_b);

    useKernel<<<1,1>>>(d_b);

    cudaDeviceSynchronize();

    cudaFree(d_b);
    cudaFree(d_a);
    delete ab;
}
#包括
结构A{
__主机设备__
虚拟void foo(){
printf(“A\n”);
}
};
结构B:公共A{
B(inti=13):数据(i){
__主机设备__
虚拟void foo()重写{
printf(“B%d\n”,数据);
}
int数据;
};
模板
__全球的__
void fixKernel(T*ptr){
T tmp(*ptr);
新(ptr)T(tmp);
}
__全球的__
无效useKernel(A*ptr){
ptr->foo();
}
int main(){
A A;
a、 foo();
B(7);
b、 foo();
A*ab=新的B();
ab->foo();
A*d_A;
cudaMalloc(&d_a,sizeof(a));
cudaMemcpy(d_a,&a,sizeof(a),cudamemcpyhostodevice);
B*d_B;
库达马洛克(d_b,sizeof(b));
cudaMemcpy(d_b和b,sizeof(b),cudamemcpyhostodevice);
固定核(d_a);
useKernel(d_a);
固定核(d_b);
useKernel(d_b);
cudaDeviceSynchronize();
库达弗里(杜布);
库达弗里(杜阿);
删除ab;
}

总之,不可以。您可以在GPU上构造对象,但不能将类分派数据复制到设备上(无论如何,这样做是没有意义的)。您可以通过存储对象类型的int并在函数中执行切换来伪造多态性。它运行时效率(稍微)较低,但完全兼容二进制文件。主机和设备之间无需转换。没有特殊的构造/复制。这是我过去常做的!但在某些情况下,“真正的多态性”似乎是必要的,例如,当我构建BVH结构时,它派生自“Hitable”,并包含指向其他“Hitable”的指针两个属性:一个整数指定对象是Hitable还是BVH,一个指针指向另一个常规类。谢谢回答!但我仍然不知道“在设备上创建对象”是如何工作的,例如,我从文件中加载了一个三角形网格,创建了一堆“三角形”,然后如何在设备上“重新创建”它们?我在构建BVH结构时遇到了一些麻烦,BVH结构是“Hitable”的派生类,包含指向其他“Hitable”的指针,如果没有多态性,似乎是不可能实现的。您说:“我已经从文件中加载了一个三角形网格,并且creare是一个bu