cuFFT库提供了一个优化的且基于CUDA实现的快速傅里叶变换(FFT)。FFT在信号处理中可以将信号从时域转换到频域,逆FFT过程则相反。换句话说,一个FFT以规则的时间间隔接受信号中的序列样本并作为输入。然后使用这些样本生成一组叠加的分量频率,按频率抽取作为输入样本的信号。如下图所示,两个信号叠加生成信号cos(x)+cos(2x),并通过FFT将两个信号的分量转为频率1.0和2.0。至于FFT其他的内容,这里不做介绍。
使 用 cuFFT API
cuFFT通常指两个独立的库:核心高性能的cuFFT库和可移植的cuFFTW库。cuFFT库是在CUDA中能提供自身API的FFT实现。另一方面,cuFFTW与标准的FFTW(快速傅里叶变换的标准C语言程序集)主机端FFT库有相同的API。和cuBLAS与传统的BLAS库共享大部分的API的情况类似,cuFFW则是用来最大限度地提高使用FFTW现有代码的可移植性。FFTW库的很多函数在cuFFTW中同样适用。此外,cuFFTW库假设所有要传输的输入数据都存储在主机内存中,并为用户处理所有的内存分布(cudaMalloc)和内存拷贝(cudaMemcpy)。虽然这可能对性能有所影响,但它大大加快了程序移植的过程。至于cuFFTW和cuFFT支持的操作,请参阅cuFFT用户指南。
cuFFT库的配置是用FFT plan完成的。即cuFFT是用来指代它的操作术语。一个plan定义了一个要进行的单一变换操作。cuFFT使用plan来获取内存分配、内存转移以及内存启动来执行变换请求。不同的plan创建函数可以用来生成增大复杂性和维数的plan:
cufftResult cufftPlan1d(cufftHandle *plan,int nx,cufftType type,int batch); cufftResult cufftPlan2d(cufftHandle *plan,int nx,int ny,cufftType type); cufftResult cufftPlan3d(cufftHandle *plan,int nx,int ny,int nz,cufftType type);
cuFFT还支持多种输入和输出数据类型,包括以下几种:复数到复数、实数到复数、复数到实数。当然,对于许多实际的应用程序,最实用的是实数到复数这种类型,它允许我们从实际系统输入实际的测量结果到cuFFT中。
一旦配置好一个cuFFT plan,使用cufftExec* 函数来对它进行调用执行(例如,cufftExecC2C)。一般来说,无论该变换是一种正向FFT(时域到频域)还是怒向FFT(频域到时域),函数调用都可以将plan、输入数据的存储位置、输出数据的存放位置作为输入。
cuFFT 功 能 示 范
一个cuFFT应用程序的工作流的不同取决于变换的复杂性。一个cuFFT应用程序的工作流一般应包括:
1.创建并配置一个cuFFT plan;
2.用cudamalloc函数分配设备内存来存储输入样本和输出频率。注意,所分配的内存必须支持对应执行的变换类型(例如,复数到复数、实数到复数、复数到实数)我们可以使用相同的设备内存对输入和输出直接进行变换;
3.用cudaMemcpy用输入样本传送设备内存;
使用cudaExec* 函数执行plan;
用cudaMemcpy取回设备内存中的结果;
用cudaFree和cufftDestroy释放CUDA和cuFFT资源;
接下来的示例代码从函数cos(x)中生成一个输入样本序列,将其转换为复数后传给GPU,在将结果拷贝回主机端之前执行复数到复数的一维plan。值得注意的是,对于输入和输出参数,因为可以将相同的内存位置dComplexSamples传给cufftExecC2C,所以这是一个就地FFT运算。可以预先分配一个独立的输出缓存区,并用来存储输出结果。
// Setup the cuFFT plan cufftPlan1d(&plan, N, CUFFT_C2C, 1); // Allocate device memory cudaMalloc((void **)&dComplexSamples, sizeof(cufftComplex) * N); // Transfer inputs into device memory cudaMemcpy(dComplexSamples, complexSamples, sizeof(cufftComplex) * N, cudaMemcpyHostToDevice); // Execute a complex-to-complex 1D FFT cufftExecC2C(plan, dComplexSamples, dComplexSamples, CUFFT_FORWARD); // Retrieve the results into host memory cudaMemcpy(complexFreq, dComplexSamples, sizeof(cufftComplex) * N, cudaMemcpyDeviceToHost);