簡體   English   中英

有沒有辦法將 vtable 從主機復制到設備(CUDA 和 C++)

[英]Is There Any Way To Copy vtable From Host To Device (CUDA & C++)

似乎 Cuda 不允許我“將源自虛擬基類的 class 的 object 傳遞給__global__函數”,原因與“虛擬表”或“虛擬指針”有關。

我想知道有什么方法可以讓我手動設置“虛擬指針”,這樣我就可以使用多態性了嗎?

有沒有辦法將 vtable 從主機復制到設備

您不想將 vtable 從主機復制到設備。 主機上的vtable(即在主機上創建的object)在vtable中有一組主機function指針。 當您將這樣的 object 復制到設備時,vtable 不會更改或“修復”,因此您最終會在設備上得到 object,其 vtable 中充滿了主機指針。

如果您然后嘗試調用其中一個虛擬功能(使用設備上的 object,來自設備代碼),就會發生不好的事情。 vtable 中列出的數字 function 入口點是在設備代碼中沒有任何意義的地址。

這樣我就可以使用多態性

我建議在設備代碼中使用多態性的方法是在設備上創建 object 這將使用一組設備 function 指針而不是主機 function 指針設置 vtable,諸如此類的問題表明它有效。 一階近似,如果你有辦法在主機代碼中創建一組多態對象,我不知道你為什么不能在設備代碼中使用類似的方法。 這個問題確實與互操作性有關——在主機和設備之間移動這些對象——這就是編程指南中所述的限制所指的內容。

我想知道有什么方法可以讓我手動設置“虛擬指針”

可能有。 為了分享知識,我將概述一種方法。 但是,我不知道 C++ 足以說明這是否可以接受/合法。 我唯一能說的是在我非常有限的測試中,它似乎有效。 但我認為這是不合法的,所以我不建議您將此方法用於實驗以外的任何事情。 即使我們不解決它是否合法,已經有一個聲明的 CUDA 限制(如上所述),您不應該嘗試在主機和設備之間傳遞具有虛擬功能的對象。 所以我提供它只是作為一個觀察,這可能對實驗或研究很有趣。 我不建議將它用於生產代碼。

此線程中概述了基本思想。 這是基於這樣的想法,即普通的對象副本似乎不會復制虛擬 function 指針表,這對我來說是有意義的,但 object 作為一個整體確實包含該表。 因此,如果我們使用這樣的方法:

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
}

it seems to be possible to take a given object, create a new "dummy" object of that type, and then "fix up" the vtable by doing a pointer-based copy of the object (considering the entire object size) rather than a “典型”對象副本。 使用它需要您自擔風險。 這個博客也可能是有趣的閱讀,雖然我不能保證那里的任何陳述的正確性。

除此之外, 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM