2013-07-04 56 views
1

我想通過編寫基本代碼學習CUDA,希望能夠將我的現有C++代碼轉換爲CUDA(用於研究)。CUDA - 簡單的複數乘法

我需要做一些複雜的數字操作,所以我寫了這個非常基本的代碼,以便在GPU內核中將實數 與一系列複數相乘。

#include <complex> 
#include <iostream> 
#include <cmath> 
#include "cuda.h" 
#include "math.h" 
#include "cuComplex.h" 

#define n 5 

using namespace std; 

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); } 
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true) 
{ 
    if (code != cudaSuccess) 
    { 
     fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); 
     if (abort) exit(code); 
    } 
} 

__global__ void func(double *s, cuDoubleComplex *j, cuDoubleComplex *calc) { 

    int tid = blockIdx.x; 

    calc[tid] = cuCmul(j[tid], make_cuDoubleComplex(*s, 0)); 

} 

int main(void) { 


    cuDoubleComplex calc[n+1], *dev_j, *dev_calc; 
    double *dev_s, s[n+1] = { 2.0, 2.0, 2.0, 2.0, 2.0 }; 
    //complex<double> j[n+1] 
    cuDoubleComplex j[n+1]; 

    for (int i = 1; i <= n; i++) { 
     j[i] = make_cuDoubleComplex(0, 5); 
     cout << "\nJ cout = " << cuCreal(j[i]) << ", " << cuCimag(j[i]); 
    } 

    // allocate the memory on the GPU 
    cudaMalloc((void**)&dev_s, (n+1) * sizeof(double)); 
    cudaMalloc((void**)&dev_j, (n+1) * sizeof(double)); 
    cudaMalloc((void**)&dev_calc, (n+1) * sizeof(double)); 

    cudaMemcpy(dev_s, s, (n+1) * sizeof(double), cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_j, j, (n+1) * sizeof(double), cudaMemcpyHostToDevice); 

    func<<<n,1>>>(dev_s, dev_j, dev_calc); 
    //kernel<<<1,1>>>(a_d); 
    gpuErrchk(cudaPeekAtLastError()); 
    gpuErrchk(cudaMemcpy(calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost)); 

    //cudaMemcpy(calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost); 

    for (int i = 1; i <= n; i++) { 
     cout << "\nCALC cout = " << cuCreal(calc[i]) << ", " << cuCimag(calc[i]); 
    } 

    return 0; 
} 

最終答案是錯誤的,我也發現了一些其他地方,我沒有得到預期的價值。

1)我期待在下面的代碼行之後爲'j'的所有元素設置一個複雜的(0,5i)雙數組。但是,我獲得全0。這是爲什麼? 2)爲什麼我不能用cout打印數組?下面顯示的代碼行提供了以下錯誤:沒有運算符「< <」與這些操作數匹配。我怎樣才能解決這個問題,而不使用printf?

cout << "\nJ = " << j[i]; 

3)GPU功能 'FUNC' 應該給出作爲最終答案的(0,10I)的陣列是給隨機值,如這些:

CALC = -1.#QNAN0 
CALC = -1.#QNAN0 
CALC = -9255963134931783100000000...000.. etc 
CALC = -9255963134931783100000000...000.. etc 

4)對於我的實際研究中,複數組'j'將以複數(雙)而非cuDoubleComplex的格式給出。我可以使用函數'func'對'j'複數(雙精度)數組執行類似的操作嗎?如果不是,我有什麼選擇?

我想我已經很好地解釋了自己,但隨時可以提出任何後續問題。 新到C++以及CUDA所以是很好的:d

+0

當你試圖通過一個實數的GPU內核乘以* *單複數發生了什麼? – Beta

回答

1

在編寫CUDA代碼,尤其是當你在學習或有困難的(事情不工作,你期望的方式),你應該總是做cuda error checking所有CUDA API調用和內核調用。

我不認爲在你的代碼中實際上存在任何CUDA功能錯誤(做得好!),但值得指出。

您的大部分問題都是由於您沒有正確打印出cuDoubleComplex這一類型。您的printf語句指定float格式參數(%f),但您未通過float值(您正在傳遞cuDoubleComplex值)。這不會起作用,並且printf會在您執行此操作時出現奇怪行爲,而不會給出任何錯誤指示。

相反,嘗試這樣的事:

printf("\nJ = %f, %f", cuCreal(j[i]), cuCimag(j[i])); 

這些功能(​​和cuCimag)返回cuComplex號的實部和虛部,以及它們返回它們作爲一個合適的類型,floatdouble,並在這種情況下,從doublefloat的內隱投射對你正在做的事情是可以的,並且可以通過printf來處理(雖然它不是很好的編程練習 - 而是使用正確的printf格式說明符來表示double值)。

如果您對這兩個printf語句進行了更改,我認爲您會得到預期的結果 - 至少我在運行代碼時執行了此操作。如果你仍然有垃圾,那麼你的CUDA GPU可能不能正常工作,這裏是我提到的CUDA錯誤檢查將幫助你發現問題所在。

關於您的問題cout,答案大致相當於我對printf發生了什麼的解釋。 cout不理解類型cuDoubleComplex,因此會引發錯誤。如果您想在不使用printf的情況下修復此問題,請使用上述printf聲明中指出的轉換函數將您的cuDoubleComplex轉換爲其個別的實部和虛部,用floatdouble表示。

關於你的最後一個問題,將complex數據轉換爲cuDoubleComplex類型應該不難。編寫一個轉換函數,根據你在cuComplex.h中的實用程序來完成它有一些後門的方法,但它們不是很好的編程習慣。

編輯:針對後續問題,當前發佈的代碼中還有兩處錯誤。

  1. dev_jdev_calccuDoubleComplex型的,但你在做這些的數量,如果他們大小doublecudaMalloccudaMemcpy。在下面的代碼中,我將這些sizeof(double)條目更改爲sizeof(cuDoubleComplex)
  2. 對於C和C++,您的索引一般來說有點奇怪。通常指數從零開始。你有一個索引問題,最後一個元素沒有得到正確的計算。我將所有索引更改爲零。

這是你的代碼的修改,對我的作品:

//#include <complex> // not necessary for this code 
#include <iostream> 
#include <cmath> 
//#include "cuda.h" // not necessary when compiling with nvcc 
#include "math.h" 
#include "cuComplex.h" 

#define n 5 

using namespace std; 

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); } 
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true) 
{ 
    if (code != cudaSuccess) 
    { 
     fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); 
     if (abort) exit(code); 
    } 
} 

__global__ void func(double *s, cuDoubleComplex *j, cuDoubleComplex *calc) { 

    int tid = blockIdx.x; 

    calc[tid] = cuCmul(j[tid], make_cuDoubleComplex(*s, 0)); 

} 

int main(void) { 


    cuDoubleComplex calc[n+1], *dev_j, *dev_calc; 
    double *dev_s, s[n] = { 2.0, 2.0, 2.0, 2.0, 2.0 }; 
    //complex<double> j[n+1] 
    cuDoubleComplex j[n]; 

    for (int i = 0; i < n; i++) { 
     j[i] = make_cuDoubleComplex(0, 5); 
     cout << "\nJ cout = " << cuCreal(j[i]) << ", " << cuCimag(j[i]); 
    } 

    // allocate the memory on the GPU 
    cudaMalloc((void**)&dev_s, (n) * sizeof(double)); 
    cudaMalloc((void**)&dev_j, (n) * sizeof(cuDoubleComplex)); 
    cudaMalloc((void**)&dev_calc, (n) * sizeof(cuDoubleComplex)); 

    cudaMemcpy(dev_s, s, (n) * sizeof(double), cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_j, j, (n) * sizeof(cuDoubleComplex), cudaMemcpyHostToDevice); 

    func<<<n,1>>>(dev_s, dev_j, dev_calc); 
    //kernel<<<1,1>>>(a_d); 
    gpuErrchk(cudaPeekAtLastError()); 
    gpuErrchk(cudaMemcpy(calc, dev_calc, (n) * sizeof(cuDoubleComplex), cudaMemcpyDeviceToHost)); 

    //cudaMemcpy(calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost); 

    for (int i = 0; i < n; i++) { 
     cout << "\nCALC cout = " << cuCreal(calc[i]) << ", " << cuCimag(calc[i]); 
    } 

    return 0; 
} 
+0

cuComplex.h中複雜類型的數據佈局與主機上C/C++/Fortran代碼中用於內置複雜類型的數據佈局兼容,因此不需要進行顯式轉換。基本上覆雜類型是結構,其實部分後面跟着虛部,並且都是IEEE-754單精度或雙精度數。其他軟件環境可以將複雜數據存儲在類似SOA的佈置中,其中實部和虛部分離爲不同;在這些情況下,將需要重新洗牌數據。 – njuffa

+0

我將printf改爲: cout <<「\ nCALC cout =」<< cuCreal(calc [i])<<「,」<< cuCimag(calc [i]); 這給出了'j'的正確答案,但'calc'沒有。我還包括了錯誤檢查代碼,並且它也沒有返回任何錯誤(不是我知道的) – user2550888

+0

這很可能是錯誤的與您的GPU設置。你能夠運行其他GPU代碼嗎?使用顯示錯誤的新代碼編輯原始問題,然後我會查看。就像我說的,只有對'printf'語句的更改,我才能夠使您的代碼正常工作。 –