【问题标题】:In place real to complex FFT with cufft用袖带实现实到复 FFT
【发布时间】:2019-11-13 09:01:40
【问题描述】:

我正在尝试使用 cufft 执行就地实数到复数 FFT。 我知道类似的问题How to perform a Real to Complex Transformation with cuFFT。但是,我在尝试重现相同的方法时遇到问题。

如果我做一个不合适的转换,没有问题,但是一旦我做到位,我在 FFT 中没有正确的值(用 python 检查,在两者之间使用二进制文件)。我没有错误,只是不正确的值。

这是我的代码:

void fftCuda2d(mat3d* scene)
{
    cufftResult resultStatus;
    cudaError_t cuda_status;

    cufftHandle plan_forward;

    resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
    cout << "Creating plan forward: " << _cudaGetErrorEnum(resultStatus) << endl;

    cufftComplex *d_fft, *d_scene, *h_fft;

    size_t size_fft = (int(scene->_width/2)+1)*scene->_height;

    cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
    cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);


    h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);

    cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);

    resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);

    cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftReal)*scene->_height*scene->_width, cudaMemcpyDeviceToHost);

    FILE* *pFileTemp;

    pFileTemp = fopen("temp.bin", "wb");

    check = fwrite(h_fft, sizeof(cufftComplex), sizeFft, pFileTemp);

}

如果我使用resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft); 并保存d_fft 的输出,我会得到正确的结果。 所以你看到我这里有什么错误吗?

P.S Mat3d 是一个结构,其中 _width 和 _height 包含矩阵的大小,而 pData 是指向数据的指针,但这没有问题。

【问题讨论】:

    标签: c++ cuda cufft


    【解决方案1】:

    (看起来这应该是一个重复的问题,但我找不到重复的问题。)

    使用就地转换时,您的输入数据需要以不同方式组织(填充)。这在 2D 情况下尤其明显,因为必须填充每一行数据。

    在非就地 R2C 转换中,输入数据是实数值的,大小为高*宽(例如 R=4,C=4 的情况):

    X X X X
    X X X X
    X X X X
    X X X X
    

    上述数据将完全占据16*sizeof(cufftReal)(假设float输入数据,维度R = 4,C = 4),它将在内存中以线性方式组织,没有间隙强>。但是,当我们切换到就地变换时,输入缓冲区的大小会发生变化。这种大小的变化对数据排列产生了影响。具体来说,输入缓冲区的大小是R*(C/2 + 1)*sizeof(cufftComplex)。对于 R=4, C=4 示例情况,即12*sizeof(cufftComplex)24*sizeof(cufftReal),但它仍然组织为 4 行数据。因此,每一行的长度为 6(如果以 cufftReal 衡量)或 3(如果以 cufftComplex 衡量)。将其视为cufftReal,那么当我们创建我们的输入数据时,我们必须像这样组织它:

    X X X X P P
    X X X X P P
    X X X X P P
    X X X X P P
    

    P 位置是“填充”数据,而不是您的输入数据。如果我们在内存中线性查看它,它看起来像:

    X X X X P P X X X X P P X X X X P P X X X X P P
    

    That is the expectation/requirement of CUFFT(我相信 FFTW 也是如此)。但是,由于您未更改存储数据的方式,因此您提供的数据如下所示:

    X X X X X X X X X X X X X X X X P P P P P P P P 
    

    这两种模式的差异是导致结果输出差异的原因。有多种方法可以解决此问题。我将选择演示使用cudaMemcpy2D 在就地情况下填充设备输入缓冲区,这将为我们提供所需的模式。这可能不是最好/最快的方式,具体取决于您的应用程序需求。

    您也没有将正确大小的结果数据从设备复制回主机。

    这是一个固定的例子:

    $ cat t1589.cu
    #include <cufft.h>
    #include <iostream>
    #include <cstdlib>
    
    struct mat3d{
      int _width;
      int _height;
      cufftReal *_pData;
    };
    
    
    void fftCuda2d(mat3d* scene)
    {
        cufftResult resultStatus;
        cudaError_t cuda_status;
    
        cufftHandle plan_forward;
    
        resultStatus = cufftPlan2d(&plan_forward, scene->_height, scene->_width, CUFFT_R2C);
        std::cout << "Creating plan forward: " <<  (int)resultStatus << std::endl;
    
        cufftComplex *d_fft, *d_scene, *h_fft;
    
        size_t size_fft = (int(scene->_width/2)+1)*scene->_height;
    
        cudaMalloc((void**)&d_scene, sizeof(cufftComplex)*size_fft);
        cudaMalloc((void**)&d_fft, sizeof(cufftComplex)*size_fft);
    
    
        h_fft = (cufftComplex*) malloc(sizeof(cufftComplex)*size_fft);
    
    #ifdef USE_IP
        cuda_status = cudaMemcpy2D(d_scene, ((scene->_width/2)+1)*sizeof(cufftComplex), scene->_pData, (scene->_width)*sizeof(cufftReal), sizeof(cufftReal) * scene->_width, scene->_height, cudaMemcpyHostToDevice);
        resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_scene);
        cuda_status = cudaMemcpy(h_fft, d_scene, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
    #else
        cuda_status = cudaMemcpy(d_scene, scene->_pData, sizeof(cufftReal) * scene->_height * scene->_width, cudaMemcpyHostToDevice);
        resultStatus = cufftExecR2C(plan_forward, (cufftReal*) d_scene, d_fft);
        cuda_status = cudaMemcpy(h_fft, d_fft, sizeof(cufftComplex)*size_fft, cudaMemcpyDeviceToHost);
    #endif
        std::cout << "exec: " <<  (int)resultStatus << std::endl;
    
        for (int i = 0; i < size_fft; i++)
          std::cout << h_fft[i].x << " " << h_fft[i].y << ",";
        std::cout << std::endl;
    }
    const int dim = 4;
    int main(){
    
      mat3d myScene;
      myScene._pData  = new cufftReal[dim*dim];
      myScene._width  = dim;
      myScene._height = dim;
      for (int i = 0; i < dim*dim; i++) myScene._pData[i] = rand()/(float)RAND_MAX;
      fftCuda2d(&myScene);
      std::cout << cudaGetErrorString(cudaGetLastError()) << std::endl;
    }
    $ nvcc -lineinfo -o t1589 t1589.cu -lcufft
    t1589.cu(15): warning: variable "cuda_status" was set but never used
    
    $ ./t1589
    Creating plan forward: 0
    exec: 0
    9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
    no error
    $ nvcc -lineinfo -o t1589 t1589.cu -lcufft -DUSE_IP
    t1589.cu(15): warning: variable "cuda_status" was set but never used
    
    $ ./t1589
    Creating plan forward: 0
    exec: 0
    9.71338 0,-0.153554 1.45243,0.171302 0,0.878097 0.533959,0.424595 -0.834714,0.858133 -0.393671,-0.205139 0,-0.131513 -0.494514,-0.165712 0,0.878097 -0.533959,0.0888268 1.49303,0.858133 0.393671,
    no error
    $
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-03
      • 2019-10-17
      • 2018-03-16
      • 1970-01-01
      • 2017-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多