簡體   English   中英

從cuda內核中訪問類數據成員 - 如何設計正確的主機/設備交互?

[英]Accessing class data members from within cuda kernel - how to design proper host/device interaction?

我一直在嘗試將一些cuda / C代碼轉換為更多OO代碼,但是我目前對cuda功能機制的理解似乎並不容易實現。 在這種情況下,我無法找到好的解釋。 畢竟這可能是不可能的。

我有一個myClass類的全局對象,它包含一個要在內核中填充的數組。

如何定義myClass中的方法,以便從設備可以看到數組和布爾成員,然后可以數組復制回主機 我使用的是cuda 7.5,我卡的計算能力是3.5。

這是描述情況的暫定結構:

#include <cstdio>
#include <cuda.h>
#include <cuda_runtime.h>

class myClass
{
public:
        bool bool_var;    // Set from host and readable from device
        int  data_size;   // Set from host
        __device__ __host__ myClass();
        __device__ __host__ ~myClass();
        __host__ void setValues(bool iftrue, int size);
        __device__ void dosomething(int device_parameter);
        __host__ void export();

        // completely unknown methods
        __host__ void prepareDeviceObj();
        __host__ void retrieveDataToHost();
private:
        int *data; // Filled in device, shared between threads, at the end copied back to host for data output
};

__host__ __device__ myClass::myClass()
{
}

__host__ __device__ myClass::~myClass()
{
#ifdef __CUDACC__
        if(bool_var)
                cudaFree(data);
#else
        free(data);
#endif
}

__host__ void myClass::setValues(bool iftrue, int size)
{
        bool_var  = iftrue;
        data_size = size;
}

__device__ void myClass::dosomething(int idx)
{
        int toadd = idx+data_size;
        atomicAdd(&data[idx], toadd); // data should be unique among threads
}


__global__ void myKernel(myClass obj)
{
        const int idx = blockIdx.x*blockDim.x + threadIdx.x;
        if(idx < obj.data_size)
        {
                if(!obj.bool_var)
                        printf("Object is not up to any task here!");
                else
                {
                        printf("Object is ready!");
                        obj.dosomething(idx);
                }
        }
}


myClass globalInstance;

int main(int argc, char** argv)
{
        int some_number = 40;
        globalInstance.setValues(true, some_number);
        globalInstance.prepareDeviceObj();           // unknown
        myKernel<<<1,some_number>>>(globalInstance); // how to pass the object?
        globalInstance.retrieveDataToHost();         // unknown
        globalInstance.export();
        exit(EXIT_SUCCESS);
}

你的方法應該是可行的。 當您按值傳遞對象作為內核參數時(如您所示),實際上沒有太多設置需要與從主機到設備的傳輸相關聯。

您需要在主機和設備上正確分配數據,並在適當的位置使用cudaMemcpy類型的操作來移動數據,就像在普通的CUDA程序中一樣。

在您完成時在全局范圍內聲明對象時要注意的一件事是,建議不要在對象的構造函數或析構函數中使用CUDA API調用。 原因在這里介紹 ,我在此不再贅述。 盡管該處理主要關注於在main之前啟動的內核,但CUDA延遲初始化還可以影響在main范圍之外執行的任何CUDA API調用,這適用於在全局范圍內實例化的對象的構造函數和析構函數。

接下來是你所展示的一個充實的例子。 我大多數時候都沒有改變你已編寫的代碼,只是添加了一些你沒有編寫的方法定義。 這里顯然有很多不同的可能方法。 有關更多示例,您可能需要查看CUDA C ++集成示例代碼

以下是您展示的內容的實例:

$ cat t1236.cu
#include <cstdio>

class myClass
{
public:
        bool bool_var;    // Set from host and readable from device
        int  data_size;   // Set from host
        __host__ myClass();
        __host__ ~myClass();
        __host__ void setValues(bool iftrue, int size);
        __device__ void dosomething(int device_parameter);
        __host__ void export_data();

        // completely unknown methods
        __host__ void prepareDeviceObj();
        __host__ void retrieveDataToHost();
private:
        int *data; // Filled in device, shared between threads, at the end copied back to host for data output
        int *h_data;
};

__host__ myClass::myClass()
{
}

__host__ myClass::~myClass()
{
}

__host__ void myClass::prepareDeviceObj(){
        cudaMemcpy(data, h_data, data_size*sizeof(h_data[0]), cudaMemcpyHostToDevice);
}
__host__ void myClass::retrieveDataToHost(){
        cudaMemcpy(h_data, data, data_size*sizeof(h_data[0]), cudaMemcpyDeviceToHost);
}

__host__ void myClass::setValues(bool iftrue, int size)
{
        bool_var  = iftrue;
        data_size = size;
        cudaMalloc(&data, data_size*sizeof(data[0]));
        h_data = (int *)malloc(data_size*sizeof(h_data[0]));
        memset(h_data, 0, data_size*sizeof(h_data[0]));
}

__device__ void myClass::dosomething(int idx)
{
        int toadd = idx+data_size;
        atomicAdd(&(data[idx]), toadd); // data should be unique among threads
}
__host__ void myClass::export_data(){
        for (int i = 0; i < data_size; i++) printf("%d ", h_data[i]);
        printf("\n");
        cudaFree(data);
        free(h_data);
}


__global__ void myKernel(myClass obj)
{
        const int idx = blockIdx.x*blockDim.x + threadIdx.x;
        if(idx < obj.data_size)
        {
                if(!obj.bool_var)
                        printf("Object is not up to any task here!");
                else
                {
                        //printf("Object is ready!");
                        obj.dosomething(idx);
                }
        }
}


myClass globalInstance;

int main(int argc, char** argv)
{
        int some_number = 40;
        globalInstance.setValues(true, some_number);
        globalInstance.prepareDeviceObj();
        myKernel<<<1,some_number>>>(globalInstance);
        globalInstance.retrieveDataToHost();
        globalInstance.export_data();
        exit(EXIT_SUCCESS);
}
$ nvcc -o t1236 t1236.cu
$ cuda-memcheck ./t1236
========= CUDA-MEMCHECK
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
========= ERROR SUMMARY: 0 errors
$

對我來說最有效的方法是只在CUDA C( .cu )文件中放置常規CUDA函數,內核和內核啟動,然后在其上構建面向對象的接口,使用C ++(. .cpp )文件中的類。

因此,在類構造函數中,調用.cu文件中的函數來分配和初始化內存,並在方法中調用啟動內核的函數等。

這也可以使開發周期更快,因為您可以經常更改類而不會導致重新編譯.cu文件,這比編譯純.cpp文件要慢得多。

暫無
暫無

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

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