Cuda 为什么卡夫特这么慢?

Cuda 为什么卡夫特这么慢?,cuda,computer-vision,gpu,fft,fftw,Cuda,Computer Vision,Gpu,Fft,Fftw,我希望加速一个计算机视觉应用程序,在英特尔CPU上使用FFTW和OpenMP计算许多FFT。然而,对于各种FFT问题大小,我发现使用OpenMP时,cuFFT比FFTW慢 在下面的实验和讨论中,我发现对于批量2D FFT,cuFFT比FFTW慢为什么cuFFT跑得这么慢,我能做些什么让cuFFT跑得更快吗? 实验() 我们的算法需要在一组大小为256x256的小平面上进行前向FFT。我在深度为32的特性上运行FFT,所以我使用批处理模式对每个函数调用执行32个FFT。通常,我使用32的批处理大

我希望加速一个计算机视觉应用程序,在英特尔CPU上使用FFTW和OpenMP计算许多FFT。然而,对于各种FFT问题大小,我发现使用OpenMP时,cuFFT比FFTW慢

在下面的实验和讨论中,我发现对于批量2D FFT,cuFFT比FFTW慢为什么cuFFT跑得这么慢,我能做些什么让cuFFT跑得更快吗?


实验() 我们的算法需要在一组大小为256x256的小平面上进行前向FFT。我在深度为32的特性上运行FFT,所以我使用批处理模式对每个函数调用执行32个FFT。通常,我使用32的批处理大小进行8次大小为256x256的FFT函数调用

FFTW+OpenMP
以下代码在
英特尔i7-2600 8核CPU上以16.0ms的速度执行

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);

其他注释

  • 在GPU版本中,CPU和GPU之间的
    cudaMemcpy
    s不包括在我的计算时间中
  • 这里给出的性能数字是几个实验的平均值,每个实验有8个FFT函数调用(总共10个实验,因此80个FFT函数调用)
  • 我尝试过许多问题大小(例如128x128、256x256、512x512、1024x1024),所有问题的深度都是32。基于
    nvvp
    profiler,某些尺寸(如1024x1024)能够使GPU完全饱和。但是,对于所有这些尺寸,CPU FFTW+OpenMP都比cuFFT快

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

      当为
      cufftPlanMany
      构建数据时,GPU的数据排列不是很好。事实上,使用32的istride和ostride意味着不合并任何数据读取。有关读取模式的详细信息,请参阅

      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-看起来像一个输入错误)


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

      如果omp版本在不同的数据区域上工作,而不是在相同的
      h_in
      h_freq
      变量上进行所有敲击(这可能是不明智的,即使是出于测试目的),会发生什么。在某种程度上,你似乎已经回答了自己的问题。对于256x256情况,您似乎在说明GPU没有得到充分利用。您可能应该测试大型案例。现在,我正在尝试一个更大的问题大小:1024x1024x32。根据英伟达剖析器( NVVP),这就饱和了GPU。现在,对于cuFFT和FFTW,我们的速度分别为620毫秒和487毫秒。在进行错位转换(h_freq和h_in是不同的内存位置)时,对于256x256x32,FFTW的平均性能从16.0毫秒降至20.2毫秒。cuFFT的性能在原地与异地保持不变,保持在21.7ms。我对
      h_in
      h_freq
      的观点是,在您的实际工作中,您不会对同一数据执行8次相同的fft运算。如果您将
      private
      指令添加到
      omp parallel for
      以要求每个线程处理
      h_in
      h_freq
      的私有副本,或者更好地在[0..7]
h_freq[0..7]中创建8个数据集
h_,会发生什么。对于我来说,出于测试目的,这似乎更为合理。否则,所有8个线程都在同一个数据集(输入和输出)上工作,这有点奇怪。@如果GPU未饱和(并且在您测试的大小下,我更关心内存限制而不是性能),则只需增加批处理大小以适应更大的容量。
    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*/));
// WAS : CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth)); 
CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth*2));