2016-07-25 57 views
0

我有一個相當複雜的C++類,它具有類myObj.fun(x,y,z)的功能。我想在一個3D網格點上的GPU上調用它。只能將一個類傳遞給CUDA內核進行並行評估?

我的高層次的問題是:通過myObj和大量的點到GPU是一件簡單的事情嗎?由於工作原因,我避免創建此函數的CUDA實現,但對我而言,這可能非常簡單。

同樣,這是一個非常高層次的問題,所以「是的,這很容易」或「不,它更復雜」是受歡迎的,儘管有點方向也會有所幫助。

回答

2

可以在GPU上使用類/對象,包括它們的方法(例如fun())。這樣的類至少必須具有用__host__ __device__修飾的方法,但代碼重構可能不會比這更多地涉及。然而,這樣的方法(像沒有任何重構的其他代碼一樣)可能不會訪問GPU的任何並行功能。最基本的比較是,在單個CPU線程中運行的方法會在單個GPU線程中運行。這通常不會更快,如果您只是將單個對象傳遞給GPU並在GPU上運行相同的單線程代碼(在單個GPU線程中),則速度通常會更慢。

一種可能的策略是,如果你有很多這些對象,或者在你的情況下有相當多的「點」,代表要獨立完成的工作,那麼你可以通過每一個(對象或點)到GPU線程,並以這種方式處理它們,以便實現GPU所喜歡的大規模多線程操作。理想情況下,你將有10,000或更多的點來處理這種方式。

由於各種原因,這仍然不是最有效的GPU使用方式,其中一個原因與高效的數據訪問有關,另一個原因與(可能的)線程分歧有關。儘管如此,有些人確實追求這種「簡單」,「不同尋常的並行」的代碼移植方法,偶爾也會有趣的加速。

根據您的實際代碼,如果您以允許相鄰線程訪問相鄰數據的方式將點傳遞給GPU,對於訪問點的每個操作,您可能會看到有吸引力的結果。期望你可以以這種方式獲得有吸引力的加速是相當合理的,可能只需要相對較少的代碼重構,但要注意數據組織以實現最佳GPU訪問。

這裏是一個完全樣例:

$ cat t30.cu 
#include <iostream> 
#include <cstdlib> 

const int dsize = 3; 
const int nTPB = 256; 
const int rng = 8; 

class myclass 
{ 

    int increment; 
    public: 
    myclass(int _incr): increment(_incr) {}; 
    // methods callable on the device need the __device__ decoration 
    __host__ __device__ void fun(int &x, int &y, int &z){ 
     x += increment; 
     y += increment; 
     z += increment;} 

}; 

// this is the actual device routine that is run per thread 
__global__ void mykernel(myclass obj, int *dx, int *dy, int *dz, int dsize){ 

    int idx = threadIdx.x+blockDim.x*blockIdx.x; // figure out which thread we are 
    if (idx < dsize) 
    obj.fun(dx[idx], dy[idx], dz[idx]); // apply method 
} 


int main(){ 

    // allocate host data 
    int *p_x, *p_y, *p_z, *d_x, *d_y, *d_z; 
    p_x = new int[dsize]; 
    p_y = new int[dsize]; 
    p_z = new int[dsize]; 

    // allocate device data 
    cudaMalloc(&d_x, dsize*sizeof(int)); 
    cudaMalloc(&d_y, dsize*sizeof(int)); 
    cudaMalloc(&d_z, dsize*sizeof(int)); 

    // initialize host data 
    std::cout << "Before:" << std::endl; 
    for (int i = 0; i < dsize; i++){ 
    p_x[i] = rand()%rng; 
    p_y[i] = rand()%rng; 
    p_z[i] = rand()%rng; 
    std::cout << p_x[i] << "," << p_y[i] << "," << p_z[i] << std::endl;} 

    // copy to device 
    cudaMemcpy(d_x, p_x, dsize*sizeof(int), cudaMemcpyHostToDevice); 
    cudaMemcpy(d_y, p_y, dsize*sizeof(int), cudaMemcpyHostToDevice); 
    cudaMemcpy(d_z, p_z, dsize*sizeof(int), cudaMemcpyHostToDevice); 

    // instantiate object on host 
    myclass test(1); 

    // copy object to device as kernel parameter 
    mykernel<<<(dsize+nTPB-1)/nTPB, nTPB>>>(test, d_x, d_y, d_z, dsize); 

    // copy data back to host 
    cudaMemcpy(p_x, d_x, dsize*sizeof(int), cudaMemcpyDeviceToHost); 
    cudaMemcpy(p_y, d_y, dsize*sizeof(int), cudaMemcpyDeviceToHost); 
    cudaMemcpy(p_z, d_z, dsize*sizeof(int), cudaMemcpyDeviceToHost); 


    std::cout << "After:" << std::endl; 
    for (int i = 0; i < dsize; i++){ 
    std::cout << p_x[i] << "," << p_y[i] << "," << p_z[i] << std::endl;} 

    return 0; 
} 
$ nvcc -o t30 t30.cu 
$ ./t30 
Before: 
7,6,1 
3,1,7 
2,4,1 
After: 
8,7,2 
4,2,8 
3,5,2 
$ 

爲了表達簡潔,我省略了proper cuda error checking但我總是建議你使用它時,你正在開發CUDA代碼。

相關問題