CUDA cuFFT使用

CUDA cuFFT的使用

1.流程

  1. 使用cufftHandle创建句柄
  2. 使用cufftPlan1d(),cufftPlan3d(),cufftPlan3d(),cufftPlanMany()对句柄进行配置,主要是配置句柄对应的信号长度,信号类型,在内存中的存储形式等信息。
    • cufftPlan1d():针对单个 1 维信号
    • cufftPlan2d():针对单个 2 维信号
    • cufftPlan3d():针对单个 3 维信号
    • cufftPlanMany():针对多个信号同时进行 fft
  3. 使用cufftExec()函数执行 fft
  4. 使用cufftDestroy()函数释放 GPU 资源

2.单个 1 维信号的 fft

假设要执行 fft的信号data_dev的长度为N,并且已经传输到 GPU显存中,data_dev数据的类型为cufftComplex,可以用一下方式产生主机段的data_dev,如下所示:

fftRowMajor.png


    cufftComplex *data_Host = (cufftComplex*)malloc(NX*BATCH*sizeof(cufftComplex));// 主机端数据头指针

 

    // 初始数据

   for (int i =0; i < NX; i++)

    {

        data_Host[i].x =float((rand() *rand()) % NX) / NX;

        data_Host[i].y =float((rand() *rand()) % NX) / NX;

    }

然后用cudaMemcpy()将主机端的data_host拷贝到设备端的data_dev,即可用下述方法执行 fft

    cufftHandle plan;// 创建cuFFT句柄

   cufftPlan1d(&plan, N, CUFFT_C2C, BATCH);

   cufftExecC2C(plan, data_dev, data_dev, CUFFT_FORWARD);// 执行 cuFFT,正变换

cufftPlan1d()

  • 第一个参数就是要配置的 cuFFT 句柄;
  • 第二个参数为要进行 fft 的信号的长度;
  • 第三个CUFFT_C2C为要执行 fft 的信号输入类型及输出类型都为复数;CUFFT_C2R表示输入复数,输出实数;CUFFT_R2C表示输入实数,输出复数;CUFFT_R2R表示输入实数,输出实数;
  • 第四个参数BATCH表示要执行 fft 的信号的个数,新版的已经使用cufftPlanMany()来同时完成多个信号的 fft。

cufftExecC2C()

  • 第一个参数就是配置好的 cuFFT 句柄;
  • 第二个参数为输入信号的首地址;
  • 第三个参数为输出信号的首地址;
  • 第四个参数CUFFT_FORWARD表示执行的是 fft 正变换;CUFFT_INVERSE表示执行 fft 逆变换。

需要注意的是,执行完逆 fft之后,要对信号中的每个值乘以 $\frac{1}{N}$

完整代码:GitHub


3. 多个 1维信号的 fft

要进行多个信号的 fft,就不得不使用 cufftPlanMany 函数,该函数的参数比较多,需要特别介绍,

cufftPlanMany(cufftHandle *plan,int rank, int *n,

             int *inembed,int istride,int idist,

             int *onembed,int ostride,int odist,

              cufftType type,int batch);

为了叙述的更准确,此处先引入一个图,表示输入数据在内存中的布局,如下图所示,数据在内存中按行优先存储,但是现有的信号为一列表示一个信号,后四列灰白色的表示无关数据,要对前 12个彩色的列信号分别进行 fft


fftColMajor.png

  • plan:表示 cufft 句柄
  • rank:表示进行 fft 的每个信号的维度数,一维信号为 1,二维信号为2,三维信号为 3 ,针对上图,rank = 1
  • n:表示进行 fft 的每个信号的行数,列数,页数,必须用数组形式表示,例如假设要进行 fft 的每个信号的行、列、页为(m, n, k),则 int n[rank] = {m, n, k};针对上图,int n[1] = {5}
  • inembed:表示输入数据的[页数,列数,行数],这是三维信号的情况;二维信号则为[列数,行数];一维信号为[行数];inembed[0] 这个参数会被忽略,也就是此处 inembed 可以为{0},{1},{2}等等。
  • istride:表示每个输入信号相邻两个元素的距离,在此处 istride = 16(每个信号相邻两个元素间的距离为16)
  • idist:表示两个连续输入信号的起始元素之间的间隔,在此处为 idist = 1(第一个信号的第一个元素与第二个信号的第一个元素的间隔为1);如果把上图数据的每一行看成一个信号,那么应该为 idist = 16;
  • onembed:表示输出数据的[页数,列数,行数],这是三维信号的情况;二维信号则为[列数,行数];一维信号为[行数];onembed[0] 这个参数会被忽略,也就是此处 onembed 可以为{0},{1},{2}等等。
  • ostride:表示每个输出信号相邻两个元素的距离,在此处 ostride = 16(每个信号相邻两个元素间的距离为16)
  • odist:表示两个连续信号的起始元素之间的间隔,在此处为 odist = 1(第一个信号的第一个元素与第二个信号的第一个元素的间隔为1);如果把上图数据的每一行看成一个信号,那么应该为 odist = 16;

如下所示:是第 b个信号的 [z][y][x](表示第 z 列,第 y 行,第 x页的元素)的索引(由于 c c++ 中数组的声明方式的问题,array[X][Y][Z]表示数组有 X页,Y 行,Z 列)

‣ 1D

input[ b * idist + x * istride ] output[ b * odist + x *ostride ]

‣ 2D

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

‣ 3D

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

    /* 申请 cufft 句柄*/

    cufftHandle plan_Nfft_Many;// 创建cuFFT句柄

   constint rank =1; // 一维 fft

   int n[rank] = { Nfft };// 进行 fft 的信号的长度为 Nfft

   int inembed[1] = {0 }; // 输入数据的[页数,列数,行数](3维);[列数,行数](2维)

   int onembed[1] = { 0 };// 输出数据的[页数,列数,行数];[列数,行数](2维)

   int istride = NXWITH0;// 每个输入信号相邻两个元素的距离

   int idist =1; // 每两个输入信号第一个元素的距离

   int ostride = NXWITH0;// 每个输出信号相邻两个元素的距离

   int odist =1; // 每两个输出信号第一个元素的距离

   int batch = NX; // 进行 fft 的信号个数

   cufftPlanMany(&plan_Nfft_Many, rank, n, inembed, istride, idist,onembed, ostride, odist, CUFFT_C2C, batch);

 

    /* 核心部份 */

 

   cudaMemcpy(data_dev, data_Host, Nfft * NXWITH0 *sizeof(cufftComplex), cudaMemcpyHostToDevice);

   cufftExecC2C(plan_Nfft_Many, data_dev, data_dev, CUFFT_FORWARD);// 执行 cuFFT,正变换

   cufftExecC2C(plan_Nfft_Many, data_dev, data_dev, CUFFT_INVERSE);// 执行 cuFFT,逆变换

   CufftComplexScale<<<dimGrid2D_NXWITH0_Nfft,dimBlock2D>>>(data_dev, data_dev,1.0f / Nfft);// 乘以系数

   cudaMemcpy(resultIFFT, data_dev, Nfft * NXWITH0 *sizeof(cufftComplex), cudaMemcpyDeviceToHost);

完整代码:GitHub

参考

  1. CUDA官方文档《CUFFT Library》

This document describes CUFFT, the NVIDIA® CUDA™ Fast Fourier Transform (FFT) library. The FFT is a divide-and-conquer algorithm for efficiently computing discrete Fourier transforms of complex or real-valued data sets. It is one of the most important and widely used numerical algorithms in computational physics and general signal processing. The CUFFT library provides a simple interface for computing parallel FFTs on an NVIDIA GPU, which allows users to leverage the floating-point power and parallelism of the GPU without having to develop a custom, CUDA FFT implementation. FFT libraries typically vary in terms of supported transform sizes and data types. For example, some libraries only implement radix-2 FFTs, restricting the transform size to a power of two. The CUFFT Library aims to support a wide range of FFT options efficiently on NVIDIA GPUs. This version of the CUFFT library supports the following features: I Complex and real-valued input and output I 1D, 2D, and 3D transforms I Batch execution for doing multiple transforms of any dimension in parallel I Transform sizes up to 64 million elements in single precision and up to 128 million elements in double precision in any dimension, limited by the available GPU memory I In-place and out-of-place transforms I Double-precision (64-bit floating point) on compatible hardware (sm1.3 and later) I Support for streamed execution, enabling asynchronous computation and data movement I FFTW compatible data layouts I Arbitrary intra- and inter-dimension element strides I Thread-safe API that can be called from multiple independent host threads
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值