Cuda 为什么输入重叠会影响cuFFT性能?

Cuda 为什么输入重叠会影响cuFFT性能?,cuda,fft,cufft,Cuda,Fft,Cufft,我正在尝试使用cuFFT的回调功能动态执行输入格式转换(例如,计算8位整数输入数据的FFT,而无需首先将输入缓冲区显式转换为float)。在我的许多应用程序中,我需要计算输入缓冲区上的重叠FFT。通常,相邻FFT可能重叠FFT长度的1/4到1/8 cuFFT及其类似FFTW的接口明确支持这一点。具体地说,如果我想计算大小为32768的FFT,在连续输入之间有4096个样本的重叠,我会设置idist=32768-4096。从产生正确输出的意义上讲,该确实可以正常工作 然而,我发现以这种方式使用cu

我正在尝试使用cuFFT的回调功能动态执行输入格式转换(例如,计算8位整数输入数据的FFT,而无需首先将输入缓冲区显式转换为
float
)。在我的许多应用程序中,我需要计算输入缓冲区上的重叠FFT。通常,相邻FFT可能重叠FFT长度的1/4到1/8

cuFFT及其类似FFTW的接口明确支持这一点。具体地说,如果我想计算大小为32768的FFT,在连续输入之间有4096个样本的重叠,我会设置
idist=32768-4096
。从产生正确输出的意义上讲,该确实可以正常工作

然而,我发现以这种方式使用cuFFT时会出现奇怪的性能下降。我设计了一个测试,以两种不同的方式实现这种格式转换和重叠:

  • 明确地告诉cuFFT输入的重叠性质:如上所述,set
    idist=nfft-overlap
    。安装一个load callback函数,根据需要在提供给回调的缓冲区索引上执行从
    int8\t
    float
    的转换

  • 不要告诉cuFFT输入的重叠性质;向其放置一个数据集
    idist=nfft
    。然后,让回调函数通过计算每个FFT输入应读取的正确索引来处理重叠

  • 。为了简洁起见,我没有在这里全部复制。该程序计算出一批1024个32768点FFT,这些FFT重叠4096个样本;输入数据类型为8位整数。当我在我的机器上运行它时(使用Geforce GTX 660 GPU,在Ubuntu 16.04上使用CUDA 8.0 RC),我得到以下结果:

    executing method 1...done in 32.523 msec
    executing method 2...done in 26.3281 msec
    
    方法2明显更快,这是我所不期望的。查看回调函数的实现:

    方法1:

    template <typename T>
    __device__ cufftReal convert_callback(void * inbuf, size_t fft_index, 
        void *, void *)
    {
        return (cufftReal)(((const T *) inbuf)[fft_index]);
    }
    
    template <typename T>
    __device__ cufftReal convert_and_overlap_callback(void *inbuf, 
        size_t fft_index, void *, void *)
    {
        // fft_index is the index of the sample that we need, not taking 
        // the overlap into account. Convert it to the appropriate sample 
        // index, considering the overlap structure. First, grab the FFT 
        // parameters from constant memory.
        int nfft = overlap_params.nfft;
        int overlap = overlap_params.overlap;
        // Calculate which FFT in the batch that we're reading data for. This
        // tells us how much overlap we need to account for. Just use integer 
        // arithmetic here for speed, knowing that this would cause a problem 
        // if we did a batch larger than 2Gsamples long.
        int fft_index_int = fft_index;
        int fft_batch_index = fft_index_int / nfft;
        // For each transform past the first one, we need to slide "overlap" 
        // samples back in the input buffer when fetching the sample.
        fft_index_int -= fft_batch_index * overlap;
        // Cast the input pointer to the appropriate type and convert to a float.
        return (cufftReal) (((const T *) inbuf)[fft_index_int]);
    }
    

    因此,在为测试用例创建计划时,性能似乎会根据是否存在其他cuFFT计划而变化!使用profiler,我发现内核启动的结构在这两种情况下没有变化;内核似乎都执行得更快。我对这种效果也没有合理的解释。

    如果您指定非标准步幅(不管批处理/转换是否正确),cuFFT在内部使用不同的路径

    广告编辑2: 这很可能是GPU在GPU上调整时钟。cuFFT计划不会相互影响

    获得更稳定结果的方法:

  • 运行warmup内核(任何能让完整GPU工作的东西都是好的),然后解决问题
  • 增加批量
  • 多次运行测试并取平均值
  • 锁定GPU的时钟(在GeForce上不太可能-特斯拉可以做到)

  • 在@llukas的建议下,我向NVIDIA提交了一份关于该问题的bug报告(如果您注册为开发人员)。他们承认重叠计划的绩效较差。他们实际上指出,在这两种情况下使用的内核配置都是次优的,他们计划最终对此进行改进。没有给出预计到达时间,但很可能不会出现在下一版本中(8.0上周刚刚发布)。最后,他们说,从CUDA 8.0开始,没有解决办法让cuFFT使用更有效的方法进行跨步输入。

    如果将重叠长度更改为不同的对齐方式,会发生什么?对齐对性能很重要。@huseyintugrulbuyukisik即使有重叠,数据仍然在4096字节边界上对齐,所以我认为这不是问题。如果用内存访问效率低下来解释的话,我不希望通过手动执行重叠内存访问来击败cuFFT的性能。谢谢你的回答。你的编辑可能是对的#2;我应该做一个更严格的测试来处理时钟频率缩放的影响。我想我希望能更深入地了解为什么cuFFT在步调模式下会有这样的行为,因为这似乎有很大的改进空间。如果它是一个开源库就好了。我建议注册为nvidiadeveloper()并提交一个bug。
    executing method 1...done in 31.5662 msec
    executing method 2...done in 17.6484 msec
    executing method 2...done in 17.7506 msec
    executing method 1...done in 20.2447 msec