Hi everybody, I was having some problems with my code recently,so I decided to create this short post about using Multidimensional FFTs. My application was using both CuFFT and FFTW 2D and 3D:

  • cufftPlan2d(plan,Nx,Ny,type);
  • cufftPlan3d(plan,Nx,Ny,Nz,type);
  • fftwf_plan_dft_2d(n0,n1,fftwf_complex *in,fftwf_complex *out,sign,flags);
  • fftwf_plan_dft_3d(n0,n1,n2,fftwf_complex *in,fftwf_complex *out,sign,flags);

As you can see,almost all methods seems to be very easy to understand. In case of CuFFT,you just specify the plan,transform dimensions and type,which is usually CUFFT_C2C. The fftw routines are very similar, except you pass dimensions first and then pointers to input and output data, then you specify either FFTW_FORWARD or FFTW_BACKWARD and usually FFTW_ESTIMATE as the last arguments unless you will be doing lots of transformations. In that case you want to pass FFTW_MEASURE as the last argument. Both libraries assume row-major-order data layout and its complex data structures are very similar. You can for example allocate cufftComplex *data and pass them as (fftwf_complex*)data to the plan routine. There are however some problems you might encounter: At first, there are 3 versions of fftw:

  • fftw – This is a (double) precision version
  • fftwf – This is a (single) precision version
  • fftwl – This is a (long Double) precision version

Usually, if you are not sure, just put everywhere fftwf (fftwf_complex,fftwf_plan …) for single precision transforms. Also note that instead of passing dimensions as Nx,Ny,Nz, fftw documentation uses n0,n1,n2.  I was searching for what these dimensions mean, but I guess there is such a mess in the doc, that nobody knows exactly, so I tested it myself and found out, that its actually Nz, Ny, Nx. Now onto CuFFT, If you lookup CuFFT doc, the plan routine expects the same as FFTw,that is: Nx, Ny, Nz. Sadly,what it actually means is again:  Nz,Ny,Nx. The real mess of defining dimensions becomes even worse after reading CuFFT Doc:

  • Nx = Number of Rows
  • Ny = Number of Columns

I personally dont know how about you, but my native orientation from math tell me exactly the opposite: Nx = Number of Columns. Ny = Number of Rows. If you are not sure what I mean, take a look at this picture: This is how I personally understand dimensions of a 3D matrix:

3DMatrixFFTDims

So … If you are used to same definitions of dimensions as me (image above), you CANT  pass (Nx,Ny) to CuFFT, but (Ny,Nx). The same applies for FFTW. In case of 3D transforms, you want to pass: (Nz,Ny,Nx) to both FFTW and CuFFT. I had a horrible mess with this and even this post was revised several times.

 

The absurdity of this is obvious if you look into doc again: plan(Nx,Ny,Nz). But you have to pass actually (Nz,Ny,Nx) which is the exact opposite.

  • cufftPlan3d(&plan,Nz,Ny,Nx,CUFFT_C2C);
  • cufftPlan2d(&plan,Ny,Nx,CUFFT_C2C);
  • fftwf_plan_dft_3d(Nz,Ny,Nx,*in,*out,FFTW_FORWARD,FFTW_ESTIMATE);
  • fftwf_plan_dft_2d(Ny,Nx,*in,*out,FFTW_FORWARD,FFTW_ESTIMATE);

 But in the end, I found CuFFT to be much easier to manipulate, much faster(of course) and more reliable, which makes it my very favorite FFT Library regardless of the mess with transform dimensions :)

 

Hope its clear now :) If you would like to compile fftw libraries for Visual Studio 2013, just read the fftw doc (this article at least does make a sense) or follow this:

  1. Open start->programs->VS2013->VSTools
  2. Launch either VS2013 x64 or x86 Native tools command prompt.
  3. Navigate to your fftw download directory.
  4. Press enter after each copy&paste (change to x86 for 32-bit):

.lib files created, have fun ;) And take care about dimensions!