Using FFTW in C#


FFTW (Fastest Fourier Transform in the West) is a discrete fourier transform library that rightfully boasts its superiority. Its trick is that it generates the transform code on-the-fly, so when you use it on a variety of different computers it's able to optimize very well. Now, since most of my (recent) programming experience has been in C#, I figured I'd just use PInvoke to access the FFTW library functionality.

Anyway, to use this, just download the dll and the newest release of FFTW and start using it. I quite literally just exported all of the function calls from FFTW, so anything that's in the original documentation will work in C# as well. Which brings me to my next point: Please read through the FFTW documentation to figure out how FFTW works. Since this is just a wrapper library, the only thing I will actually explain here is the memory management and such. With that said, if you have any questions about FFTW, I'd be happy to answer them (though there are better people to ask).
Anyway, the only thing worth noting is the creation and accessing of data from within C#, if one should choose to use safe code (which I typically do).
You can either choose to create the arrays using FFTW's malloc(), which is identical to the basic malloc() except that it makes sure the data is aligned on a certain boundary. This returns an IntPtr, which means you can use Marshal.Copy to read or write data, and then the FFTW free() to release the memory.
Your other option is to declare a float[] or double[] array in C# and then create a GCHandle.Pinned object to ensure that the array does not move (and use the GetAddrOfPinnedObject() to get the actual IntPtr).
Using malloc(), the actual transform is faster by up to 3x (but not always!), and you know you're using an FFTW-friendly method. However, you need to store two copies of the data that you copy back and forth using Marshal.Copy, which takes up space and time. Using GCHandle.Pinned, however, the behavior is less predictable. I was doing speed tests, and some arrays seemed to be up to 2-3x slower than their GCHandle.Pinned counterparts while others were indistinguishable. I assume this was due to whatever alignment, but I could find no good way of fixing it. If anybody has a solution short of manually aligning via unsafe code, do let me know.

So, what's the speed loss of using FFTW from C#, you ask? Well, the bulk of it (aside from the aformentioned memory issues) is the PInvoke overhead. This is the nonnegligible amount of time it takes .NET to call an external function, and the time you will care about it is when you call FFTW's execute() function. Basically, the fewer times you make that call, the better. Most of the time you should be okay, but if you're planning on doing an extremely large number of small fourier transforms, you might be better off writing a C++ dll that makes the iterated calls for you, so you only call an external function once. Note that there is an advanced interface that will let you create plans for multiple consecutive FFTs provided that the arrays are spaced regularly in memory.

Since I have received a number of emails on the subject, I figured it might be useful to actually provide some sample code. So, without further ado, here is the code to create and use all types of managed and unmanaged arrays, create plans, execute them, and then clean them up afterwards.
Keep in mind that the following code demonstrates all the various types of arrays simultaneously. You will most likely not need to use each type of array, just one or two for your specific purpose. If you are receiving your data from some other library that uses unmanaged memory, you can just allocate unmanaged memory for the input array and use a memcpy call to transfer it; only use C# arrays if you plan on directly manipulating the data.


using System;
using System.Runtime.InteropServices;
using fftwlib;

namespace fftwlib_test
{
   public class fftwtest
   {
        //pointers to unmanaged arrays
        IntPtr pin, pout;

        //managed arrays
        float[] fin, fout;

        //handles to managed arrays, keeps them pinned in memory
        GCHandle hin, hout;

        //pointers to the FFTW plan objects
        IntPtr fplan1, fplan2, fplan3;

        // Initializes FFTW and all arrays
        // n: Logical size of the transform
        public void InitFFTW(int n)
        {
            //create two unmanaged arrays, properly aligned
            pin = fftwf.malloc(n*8);
            pout = fftwf.malloc(n*8);

            //create two managed arrays, possibly misalinged
            //n*2 because we are dealing with complex numbers
            fin = new float[n*2];
            fout = new float[n*2];

            //get handles and pin arrays so the GC doesn't move them
            hin = GCHandle.Alloc(fin,GCHandleType.Pinned);
            hout = GCHandle.Alloc(fout,GCHandleType.Pinned);

            //fill our arrays with a sawtooth signal
            for (int i=0;i<n*2;i++)
                fin[i] = i%50;
            for (int i=0;i<n*2;i++)
                fout[i] = i%50;

            //copy managed arrays to unmanaged arrays
            Marshal.Copy(fin,0,pin,n*2);
            Marshal.Copy(fout,0,pout,n*2);

            //create a few test transforms
            fplan1 = fftwf.dft_1d(n,pin,pout,fftw_direction.Forward,fftw_flags.Estimate);
            fplan2 = fftwf.dft_1d(n,hin.AddrOfPinnedObject(),hout.AddrOfPinnedObject(),
	fftw_direction.Forward,fftw_flags.Estimate);
            fplan3 = fftwf.dft_1d(n,hout.AddrOfPinnedObject(),pin,
	fftw_direction.Backward,fftw_flags.Measure);
        }

        public void TestAll()
        {
            TestPlan(fplan1);
            TestPlan(fplan2);
            TestPlan(fplan3);
        }

        // Tests a single plan, displaying results
        //plan: Pointer to plan to test
        public void TestPlan(IntPtr plan)
        {
            int start = System.Environment.TickCount;
            for (int i=0;i<10000;i++)
                fftwf.execute(plan);
            Console.WriteLine("Time: {0} ms",
                (System.Environment.TickCount-start));
            //a: adds, b: muls, c: fmas
            double a=0,b=0,c=0;
            fftwf.flops(plan,ref a, ref b, ref c);
            Console.WriteLine("Approx. flops: {0}",(a+b+2*c));
        }

        // Releases all memory used by FFTW/C#
        public void FreeFFTW()
        {
            //it is essential that you call these after finishing
            //may want to put the initializers in the constructor
            //and these in the destructor
            fftwf.free(pin);
            fftwf.free(pout);
            fftwf.destroy_plan(fplan1);
            fftwf.destroy_plan(fplan2);
            fftwf.destroy_plan(fplan3);
            hin.Free();
            hout.Free();
        }
    }
}


Download library (source included)

As always, questions, comments - let me know, and happy programming.
Oh, yeah, and given that I didn't really put any significant amount of work into this library, there aren't any licensing issues to speak of. Use it for whatever you like, commercial or free, blah blah blah no guarantee not liable for any damages all that stuff.

 

 

 

Copyright 2006 - Tamas Szalay.