CUFFT 淺析

1. 流程
使用cufftHandle創建句柄
使用cufftPlan1d(),cufftPlan3d(),cufftPlan3d(),cufftPlanMany()對句柄進行配置,主要是配置句柄對應的信號長度,信號類型,在內存中的存儲形式等信息。 
cufftPlan1d():針對單個 1 維信號
cufftPlan2d():針對單個 2 維信號
cufftPlan3d():針對單個 3 維信號
cufftPlanMany():針對多個信號同時進行 fft
使用cufftExec()函數執行 fft
使用cufftDestroy()函數釋放 GPU 資源
2. 單個 1 維信號的 fft
假設要執行 fft 的信號data_dev的長度爲N,並且已經傳輸到 GPU 顯存中,data_dev數據的類型爲cufftComplex,可以用一下方式產生主機段的data_dev,如下所示:

    cufftComplex *data_Host = (cufftComplex*)malloc(NX*BATCH*sizeof(cufftComplex)); // 主機端數據頭指針

    // 初始數據
    for (int i = 0; i < NX; i++)
    {
        data_Host[i].x = float((rand() * rand()) % NX) / NX;
        data_Host[i].y = float((rand() * rand()) % NX) / NX;
    }


然後用cudaMemcpy()將主機端的data_host拷貝到設備端的data_dev,即可用下述方法執行 fft :

    cufftHandle plan; // 創建cuFFT句柄
    cufftPlan1d(&plan, N, CUFFT_C2C, BATCH);
    cufftExecC2C(plan, data_dev, data_dev, CUFFT_FORWARD); // 執行 cuFFT,正變換


cufftPlan1d():

第一個參數就是要配置的 cuFFT 句柄;
第二個參數爲要進行 fft 的信號的長度;
第三個CUFFT_C2C爲要執行 fft 的信號輸入類型及輸出類型都爲複數;CUFFT_C2R表示輸入複數,輸出實數;CUFFT_R2C表示輸入實數,輸出複數;CUFFT_R2R表示輸入實數,輸出實數;
第四個參數BATCH表示要執行 fft 的信號的個數,新版的已經使用cufftPlanMany()來同時完成多個信號的 fft。
cufftExecC2C()

第一個參數就是配置好的 cuFFT 句柄;
第二個參數爲輸入信號的首地址;
第三個參數爲輸出信號的首地址;
第四個參數CUFFT_FORWARD表示執行的是 fft 正變換;CUFFT_INVERSE表示執行 fft 逆變換。
需要注意的是,執行完逆 fft 之後,要對信號中的每個值乘以 1/N

3. 多個 1 維信號的 fft
要進行多個信號的 fft,就不得不使用 cufftPlanMany 函數,該函數的參數比較多,需要特別介紹,

cufftPlanMany(cufftHandle *plan, int rank, int *n, 
              int *inembed, int istride, int idist, 
              int *onembed, int ostride, int odist, 
              cufftType type, int batch);


爲了敘述的更準確,此處先引入一個圖,表示輸入數據在內存中的佈局,如下圖所示,數據在內存中按行優先存儲,但是現有的信號爲一列表示一個信號,後四列灰白色的表示無關數據,要對前 12 個彩色的列信號分別進行 fft。

plan:表示 cufft 句柄
rank:表示進行 fft 的每個信號的維度數,一維信號爲 1,二維信號爲2,三維信號爲 3 ,針對上圖,rank = 1
n:表示進行 fft 的每個信號的行數,列數,頁數,必須用數組形式表示,例如假設要進行 fft 的每個信號的行、列、頁爲(m, n, k),則 int n[rank] = {m, n, k};針對上圖,int n[1] = {5}
inembed:表示輸入數據的[頁數,列數,行數],這是三維信號的情況;二維信號則爲[列數,行數];一維信號爲[行數];inembed[0] 這個參數會被忽略,也就是此處 inembed 可以爲{0},{1},{2}等等。
istride:表示每個輸入信號相鄰兩個元素的距離,在此處 istride = 16(每個信號相鄰兩個元素間的距離爲16)
idist:表示兩個連續輸入信號的起始元素之間的間隔,在此處爲 idist = 1(第一個信號的第一個元素與第二個信號的第一個元素的間隔爲1);如果把上圖數據的每一行看成一個信號,那麼應該爲 idist = 16;
onembed:表示輸出數據的[頁數,列數,行數],這是三維信號的情況;二維信號則爲[列數,行數];一維信號爲[行數];onembed[0] 這個參數會被忽略,也就是此處 onembed 可以爲{0},{1},{2}等等。
ostride:表示每個輸出信號相鄰兩個元素的距離,在此處 ostride = 16(每個信號相鄰兩個元素間的距離爲16)
odist:表示兩個連續信號的起始元素之間的間隔,在此處爲 odist = 1(第一個信號的第一個元素與第二個信號的第一個元素的間隔爲1);如果把上圖數據的每一行看成一個信號,那麼應該爲 odist = 16;
如下所示:是第 b 個信號的 [z][y][x] (表示第 z 列,第 y 行,第 x 頁的元素)的索引(由於 c 和 c++ 中數組的聲明方式的問題,array[X][Y][Z]表示數組有 X 頁,Y 行,Z 列) :

‣ 1D

input[ b * idist + x * istride ] 
output[ b * odist + x * ostride ]

‣ 2D

input[ b * idist + (x * inembed[1] + y) * istride ] 
output[ b * odist + (x * onembed[1] + y) * ostride ]

‣ 3D

input[b * idist + (x * inembed[1] * inembed[2] + y * inembed[2] + z) * istride] 
output[b * odist + (x * onembed[1] * onembed[2] + y * onembed[2] + z) * ostride]

    /* 申請 cufft 句柄*/
    cufftHandle plan_Nfft_Many; // 創建cuFFT句柄
    const int rank = 1; // 一維 fft
    int n[rank] = { Nfft }; // 進行 fft 的信號的長度爲 Nfft
    int inembed[1] = { 0 }; // 輸入數據的[頁數,列數,行數](3維);[列數,行數](2維)
    int onembed[1] = { 0 }; // 輸出數據的[頁數,列數,行數];[列數,行數](2維)
    int istride = NXWITH0; // 每個輸入信號相鄰兩個元素的距離
    int idist = 1; // 每兩個輸入信號第一個元素的距離
    int ostride = NXWITH0; // 每個輸出信號相鄰兩個元素的距離
    int odist = 1; // 每兩個輸出信號第一個元素的距離
    int batch = NX; // 進行 fft 的信號個數
    cufftPlanMany(&plan_Nfft_Many, rank, n, inembed, istride, idist, onembed, ostride, odist, CUFFT_C2C, batch);

    /* 核心部份 */

    cudaMemcpy(data_dev, data_Host, Nfft * NXWITH0 * sizeof(cufftComplex), cudaMemcpyHostToDevice);
    cufftExecC2C(plan_Nfft_Many, data_dev, data_dev, CUFFT_FORWARD); // 執行 cuFFT,正變換
    cufftExecC2C(plan_Nfft_Many, data_dev, data_dev, CUFFT_INVERSE); // 執行 cuFFT,逆變換
    CufftComplexScale<<<dimGrid2D_NXWITH0_Nfft, dimBlock2D>>>(data_dev, data_dev, 1.0f / Nfft); // 乘以係數
    cudaMemcpy(resultIFFT, data_dev, Nfft * NXWITH0 * sizeof(cufftComplex), cudaMemcpyDeviceToHost);

 

參考
CUDA官方文檔《CUFFT Library》

https://blog.csdn.net/endlch/article/details/46724811

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章