CUDA快速傅里叶变换(cuFFT)阅读笔记(一)

CUDA为开发人员提供了多种库,每一类库针对某一特定领域的应用,CUFFT库则是CUDA中专门用于进行傅里叶变换的函数库,这一系列的文章是博主近一段时间对cuFFT库的学习总结,主要内容是文档的译文,其间夹杂一些博主自己的理解。初学CUDA,理解有误之处在所难免,阅读本篇文章的读者如若发现请不吝指正。

1. 简介

       cuFFT是的全称是CUDA Fast Fourier Transform,顾名思义,它提供了一系列的函数帮助开发者进行快速傅里叶变换的运算。cuFFT库由两个子库构成,它们分别是CUFFT和CUFFTW。CUFFTW库是一个移植工具(porting tool),它为用户提供了一些接口,以使得用户使用FFTW库(一个非常流行的CPU快速傅里叶变换库)编写的程序能够运行在CUDA GPU上。而CUFFT则是纯CUDA接口的快速傅里叶变换库。

        在CUDA toolkit 5.5版本中,cuFFT库支持的特性有:

        1、对于可以表示为的输入规模,CUFFT会自动采用一些优化算法来达到最佳的运算性能

        2、对于所有的输入规模,CUFFT的算法复杂性均为O(nlogn)

        3、不同输入输出类型的指定:其中C2C代表输入输出均为复数,R2C代表输入为实数而输出为复数,C2R代表输入为复数而输出为实数。

        4、可以进行1维、2维和3维变换

        5、多个不同的1D、2D、3D变换可以并行进行

        6、同时支持单精度浮点和双精度浮点运算

        7、支持就地转换(输出直接覆盖输入)和外部转换(输出和输入不重叠)

        8、与FFTW库兼容的数据布局

        9、支持跨步输入数据读取

        10、支持流执行(Streamed execution),这样就同时支持了计算与数据传输的异步并行执行。

        11、对于单精度浮点数,一次最多传输128百万元素,双精度则一次最多传输64百万元素。

        12、CUFFT库提供的API是线程安全的。


2. 使用CUFFT API

        在关注具体的API之前,我们先来回顾一下快速傅里叶变换的背景知识。
        快速傅里叶变换(FFT)是离散傅里叶变换(DFT)的快速算法,在数字信号处理、大整数乘法和求解偏微分方程等领域都有着广泛的应用,DFT的作用是将时域(time domain)上的一组离散复数与到频域(frequency domain)上的一组值相互映射,以对输入数据进行进一步处理。离散傅里叶公式可以表示为:

其中,X k 是与输入数据 x k 大小相同的数组,这一公式也被称作正向(forward)DFT。把上式中e的指数符号修改为正(positive),我们就得到了逆向(inverse)DFT。CUFFT API根据N的不同,将使用不同的优化算法,以达到最佳的性能。
        CUFFT API的原型是FFTW库,FFTW则是一个基于CPU的高效FFT库。CUFFT提供了一个简单的被称为plan的配置方法,使用plan的好处是可以根据输入数据的大小预先配置好内存和计算资源,使得真正运算时处理器能达到最佳的性能。设置好plan后,运行execution函数,真正的傅里叶变换便开始执行了。一旦配置完成,配置信息便被保存起来,这对于多次调用来说非常省时。
        下面来看一个代码片段,计算BATCH块大小为NX的一维DFT的CUDA代码通常如下所示:
#define NX 256
#define BATCH 10
... {
    cufftHandle plan;
    cufftComplex *data;
    ...
    cudaMalloc((void**)&data, sizeof(cufftComplex)*NX*BATCH);
    cufftPlan1d(&plan, NX, CUFFT_C2C, BATCH);
    ...
    cufftExecC2C(plan, data, data, CUFFT_FORWARD);
    cudaThreadSynchronize();
    ...
    cufftDestroy(plan);
    cudaFree(data);
}

2.1. 访问CUFFT(Accessing CUFFT)

        CUFFT和CUFFTW均被实现为共享库,可以使用编译器和链接器把它们集成到普通的程序当中。这些库文件和头文件在不同系统上的默认位置如下图所示:


最常见的使用这些库函数的方法是修改一个已经存在的CUDA文件,以filename.cu为例,在filename.cu中将头文件cufft.h包含进来,然后在程序中调用CUFFT库函数,为了使得程序可以运行,一个单一文件的编译和链接命令的形式可以如下:



2.2. 傅里叶变换配置(Fourier Transform Setup)

        在CUDA上进行傅里叶变换一般需要做以下几步工作:
        1. 创建一个plan。调用函数cufftPlan1D/cufftPlan2D/cufftPlan3D可以分别创建一个简单的1维/2维/3维的傅里叶变换。调用函数cufftPlanMany则可以创建支持更多配置操作的变换计划。
        2. 执行plan。这一步可以使用cufftExecC2C()、cufftExecR2C()或cufftExecC2R()等函数完成plan的计算任务。
        3. 执行完成以后若不再需要该plan,则调用cufftDestroy()函数销毁该plan及为其分配的计算资源。

2.3 傅里叶变换类型(Fourier Transform Types)

        CUFFT库实现了三种不同类型的傅里叶变换:C2C(复数到复数)、C2R(复数到实数)、R2C(实数到复数)。本质上,这三种转换都可以被看做是复数域到复数域的变换,之所以这样划分,其最主要的考量是性能因素。例如,在一般的数字信号处理中,输入数据是一些离散的实数域上的采样点,这时候对它们做傅里叶变换实际上就是R2C,根据埃尔米特对称性(Hermitian symmetry),变换后,*代表共轭复数。CUFFT的傅里叶变换类型则利用了这些冗余,将计算量降到最低。

        变换执行函数的单精度和双精度版本分别定义如下:

        1. cufftExecC2C() / cufftExecZ2Z() - 单精度/双精度浮点数复数域到复数域的傅里叶变换

        2. cufftExecR2C() / cufftExecD2Z() - 单精度/双精度浮点数实数域到复数域的傅里叶变换(正向傅里叶变换)

        3. cufftExecC2R() / cufftExecZ2D() - 单精度/双精度浮点数复数域到实数域的傅里叶变换(逆向傅里叶变换)

        

2.4 数据布局(Data Layout)

        CUFFT库包含有若干种数据类型,对于复数有cufftComplex / cufftDoubleComplex两种数据类型,对于实数则分别有cufftReal / cufftDouble两种数据类型。
        根据转换结果的存储位置不同,FFT变换可分为就地变换(in-place)和外部变换(out-of-place),前者直接在输入数据上进行变换,而后者则会将变换后的结果存入新的存储器地址。
        就地转换(in-place)支持两种数据布局:native和padded,前者用于获得最佳性能,而后者则用于与FFTW库兼容。
        在padded布局中输出信号的开始地址与输入信号一样,换句话说,实数域到复数域变换的输入数据和复数域到实数域的输出数据必须被填充。在native布局中则没有填充要求。
        输入数据和输出数据的尺寸总结如下:
CUDA快速傅里叶变换(cuFFT)阅读笔记(一)_第1张图片

2.5 流化CUFFT变换(Streamed CUFFT Transform)

        CUFFT的每一个plan都可以和某个流(stream)结合起来运行,一旦结合,那么该plan就会在特定的流上面运行。CUFFT执行的流化使得快速傅里叶变换可以和其它流中的数据拷贝任务并行运行,从而提高了设备利用率和程序性能。

2.6 线程安全(Thread Safety)

        CUFFT库从版本4.1开始拥有线程安全特性,也就是说,主机端的多个线程同时调用CUFFT中的函数时,CUDA可以保证任务执行的正确性。

你可能感兴趣的:(CUDA,性能优化,GPU,异构计算)