cuda - 为什么cuFFT这么慢?

标签 cuda computer-vision gpu fft fftw

我希望在 Intel CPU 上使用 FFTW 和 OpenMP 来加速计算许多 FFT 的计算机视觉应用程序。但是,对于各种 FFT 问题大小,我发现 cuFFT 比使用 OpenMP 的 FFTW 慢。

在下面的实验和讨论中,我发现 cuFFT 比批处理 2D FFT 的 FFTW 慢。 为什么 cuFFT 这么慢,我能做些什么让 cuFFT 运行得更快?

实验 ( code download )

我们的 computer vision application需要在一堆大小为 256x256 的小平面上进行前向 FFT。我在 HOG 上运行 FFT深度为 32 的特征,因此我使用批处理模式对每个函数调用执行 32 个 FFT。通常,我执行大约 8 个大小为 256x256 的 FFT 函数调用,批处理大小为 32。

FFTW + OpenMP
以下代码在中执行16.0 毫秒 Intel i7-2600 8-core CPU 上.

int depth = 32; int nRows = 256; int nCols = 256; int nIter = 8;
int n[2] = {nRows, nCols};

//if nCols is even, cols_padded = (nCols+2). if nCols is odd, cols_padded = (nCols+1)
int cols_padded = 2*(nCols/2 + 1); //allocate this width, but tell FFTW that it's nCols width
int inembed[2] = {nRows, 2*(nCols/2 + 1)};
int onembed[2] = {nRows, (nCols/2 + 1)}; //default -- equivalent ot onembed=NULL

float* h_in = (float*)malloc(sizeof(float)*nRows*cols_padded*depth);
memset(h_in, 0, sizeof(float)*nRows*cols_padded*depth);
fftwf_complex* h_freq = reinterpret_cast<fftwf_complex*>(h_in); //in-place version

fftwf_plan forwardPlan = fftwf_plan_many_dft_r2c(2, //rank
                                                 n, //dims -- this doesn't include zero-padding
                                                 depth, //howmany
                                                 h_in, //in
                                                 inembed, //inembed
                                                 depth, //istride
                                                 1, //idist
                                                 h_freq, //out
                                                 onembed, //onembed
                                                 depth, //ostride
                                                 1, //odist
                                                 FFTW_PATIENT /*flags*/);
double start = read_timer();
#pragma omp parallel for
for(int i=0; i<nIter; i++){
    fftwf_execute_dft_r2c(forwardPlan, h_in, h_freq);
}
double responseTime = read_timer() - start;
printf("did %d FFT calls in %f ms \n", nIter, responseTime);

cuFFT
以下代码在中执行21.7ms 在顶级 NVIDIA K20 GPU .请注意,即使我使用流,cuFFT does not run multiple FFTs concurrently .
int depth = 32; int nRows = 256; int nCols = 256; int nIter = 8;
int n[2] = {nRows, nCols};

int cols_padded = 2*(nCols/2 + 1); //allocate this width, but tell FFTW that it's nCols width
int inembed[2] = {nRows, 2*(nCols/2 + 1)};
int onembed[2] = {nRows, (nCols/2 + 1)}; //default -- equivalent ot onembed=NULL in FFTW
cufftHandle forwardPlan;
float* d_in; cufftComplex* d_freq;
CHECK_CUFFT(cufftPlanMany(&forwardPlan,
              2, //rank
              n, //dimensions = {nRows, nCols}
              inembed, //inembed
              depth, //istride
              1, //idist
              onembed, //onembed
              depth, //ostride
              1, //odist
              CUFFT_R2C, //cufftType
              depth /*batch*/));

CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth));
d_freq = reinterpret_cast<cufftComplex*>(d_in);

double start = read_timer();
for(int i=0; i<nIter; i++){

    CHECK_CUFFT(cufftExecR2C(forwardPlan, d_in, d_freq));
}
CHECK_CUDART(cudaDeviceSynchronize());
double responseTime = read_timer() - start;
printf("did %d FFT calls in %f ms \n", nIter, responseTime);

其他注意事项
  • 在 GPU 版本中,cudaMemcpy CPU和GPU之间的s是不包括 在我的计算时间。
  • 此处显示的性能数字是多个实验的平均值,其中每个实验有 8 个 FFT 函数调用(总共 10 个实验,因此 80 个 FFT 函数调用)。
  • 我尝试了许多问题大小(例如 128x128、256x256、512x512、1024x1024),深度都为 32。基于 nvvp分析器中,某些尺寸(如 1024x1024)能够使 GPU 完全饱和。但是,对于所有这些大小,CPU FFTW+OpenMP 比 cuFFT 快。
  • 最佳答案

    问题可能已经过时,尽管这里有一个可能的解释(对于 cuFFT 的缓慢)。

    cufftPlanMany 构建数据时,数据排列在GPU上不是很好。事实上,使用 32 的 istride 和 ostride 意味着没有数据读取被合并。见 here有关读取模式的详细信息

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

    在这种情况下,如果 i/ostride 为 32,则不太可能合并/优化。 (确实 b 是批号)。以下是我应用的更改:
        CHECK_CUFFT(cufftPlanMany(&forwardPlan,
                  2, //rank
                  n, //dimensions = {nRows, nCols}
                  inembed, //inembed
                  1,  // WAS: depth, //istride
                  nRows*cols_padded, // WAS: 1, //idist
                  onembed, //onembed
                  1, // WAS: depth, //ostride
                  nRows*cols_padded, // WAS:1, //odist
                  CUFFT_R2C, //cufftType
                  depth /*batch*/));
    

    运行此程序,由于非法内存访问,我输入了未指定的启动失败。您可能想要更改内存分配( cufftComplex 是两个浮点数,您的分配大小需要一个 x2 - 看起来像一个错字)。
    // WAS : CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth)); 
    CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth*2)); 
    

    以这种方式运行时,我的卡上的性能得到了 x8 的提升。

    关于cuda - 为什么cuFFT这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18069017/

    相关文章:

    c++ - 配置管理器和命令行

    caching - GPU L2 缓存命中率为 100%,DRAM 加载事务有时为 0

    performance - Pytorch 中的 Titan XP 与 Quadro P400 GPU

    python - "Resource exhausted: OOM when allocating tensor"在重新训练 GPT 2 模型 : 期间

    compiler-construction - 尝试让 CUDA 工作,示例找不到 helper_cuda.h

    c++ - 在 cuda 中展开 3 个 c++ 循环

    cuda - 2D 纹理的间距对齐

    opencv - 相机平移时的单应性(用于拼接)

    opencv - OpenCV2写入正在写入黑色图像

    algorithm - 检测具有一定曲率的边缘