C# Csharp调用FFTW进行傅立叶变换官方代码解析(附官网源码代码及视频教程)

C# Csharp调用FFTW进行傅里叶变换

为了对一个星期以来工作做一个总结,也可能为大家提供一些参考
完整代码可以从官网下载:链接
我也把代码放到百度网盘了:链接 提取码:0h1x
视频教程:链接(自己熬夜录的,一看就懂)

代码部分:
首先看一下这个目录结构:
在这里插入图片描述一共有3个代码文件,我们主要关注的是FFTWtest.cs这个文件,因为interop.cs实际上是对依赖文件的一个引入,Managed.cs是一些方法的封装

以下是FFTWtest.cs的源码(含我的标注),配合视频教程食用口味更佳

using System;
using System.Runtime.InteropServices;
using FFTWSharp;

namespace FFTWSharp_test
{
    public class FFTWtest
    {
        const int sampleSize = 16384;
        const int repeatPlan = 10000;
        const bool forgetWisdom = false;  //Wisdom 说明http://www.fftw.org/fftw-wisdom.1.html

        //pointers to unmanaged arrays 指向数组的指针
        IntPtr pin, pout;       //这个现在理解成指针

        //managed arrays   管理数组
        float[] fin, fout;
        double[] din, dout;

        //handles to managed arrays, keeps them pinned in memory

        //托管数组的句柄,把他们留在数组中 ?这个GCHandle怎么理解

        //为了防止托管代码把对象回收,需要把那个对象“钉”住(pin),让它的内存地址固定,
        //而不被垃圾回收掉,然后最后我们自己管理,自己释放内存,这时候就需要GCHandle
        GCHandle hin, hout, hdin, hdout;

        //pointers to the FFTW plan objects  FFTW plan 对象的指针
        IntPtr fplan1, fplan2, fplan3, fplan4, fplan5;

        // and an example of the managed interface
        fftwf_plan mplan1, mplan2, mplan3, mplan4;  //plan对象型变量
        fftwf_complexarray mfin, mfout;  //对象型变量
        fftw_plan mplan5;
        fftw_complexarray mdin, mdout;  //复数数组

        // length of arrays
        int fftLength = 0;      //fft的长度

        // Initializes FFTW and all arrays  初始化所有的fftw和所有的数组
        // n: Logical size of the transform
        public FFTWtest(int n)                                  //构造函数
        {
            Console.WriteLine($"Start testing with n = {n.ToString("#,0")} complex numbers. All plans will be executed {repeatPlan.ToString("#,0")} times on a single thread.");
            Console.WriteLine("Please wait, creating plans...");
            fftLength = n;

            // create two unmanaged arrays, properly aligned

            //pin和pout都是IntPtr类型的
            pin = fftwf.malloc(n * 8);    //创建空间,用pin指向空间的首地址
            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];    //给数组分配存储空间,float类型
            fout = new float[n * 2];
            // and two more for double FFTW
            din = new double[n * 2];        //给数组分配存储空间,float类型
            dout = new double[n * 2];

            // get handles and pin arrays so the GC doesn't move them

            //为了防止托管代码把对象回收,需要把那个对象“钉”住(pin),让它的内存地址固定,
            //而不被垃圾回收掉,然后最后我们自己管理,自己释放内存,这时候就需要GCHandle
            
			//C#的垃圾处理机制的触发条件:
            //当处理newobj(cil 的new指令)指令时,
            //如果CLR判定托管堆没有足够的空间来分配所请求的类型,它会执行一次垃圾回收来尝试释放内存

            //意思是fin可能会被自动回收,但是hin不会回收

            // 类型说明:GCHandle hin, hout, hdin, hdout;
            hin = GCHandle.Alloc(fin, GCHandleType.Pinned);    //意思是fin可能会被自动回收,但是hin不会回收
            hout = GCHandle.Alloc(fout, GCHandleType.Pinned);
            hdin = GCHandle.Alloc(din, GCHandleType.Pinned);
            hdout = GCHandle.Alloc(dout, GCHandleType.Pinned);

            // create a few test transforms

            /// <dft_1d summary>
            /// 为一维的复数到复数的离散傅里叶变换创建一个plan
            /// Creates a plan for a 1-dimensional complex-to-complex DFT
            /// </summary>
            /// <param name="n">The logical size of the transform</param> 逻辑空间大小
            /// <param name="direction">Specifies the direction of the transform</param>指定转换的方向
            /// <param name="input">Pointer to an array of 8-byte complex numbers</param>指向复试数组的指针
            /// <param name="output">Pointer to an array of 8-byte complex numbers</param>
            /// <param name="flags">Flags that specify the behavior of the planner</param> 指定计划器行为的标志          

            ///创建各个plan用来做性能比较
            //pin->pout
            //pin和pout都是IntPtr类型
            //这为什么用的pin,不怕对象被回收么?这里的pin为什么能直接用了
            //回收的是fin?因为fin是float[]类型?

            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);
            // end with transforming back to original array 以转换回原始数组结束
            fplan4 = fftwf.dft_1d(n, hout.AddrOfPinnedObject(), hin.AddrOfPinnedObject(), fftw_direction.Backward, fftw_flags.Estimate);
            // and check a quick one with doubles, just to be sure
            fplan5 = fftw.dft_1d(n, hdin.AddrOfPinnedObject(), hdout.AddrOfPinnedObject(), fftw_direction.Backward, fftw_flags.Measure);

            // create a managed plan as well 同时创建一个管理计划
            mfin = new fftwf_complexarray(fin);  //  从浮点数组创建与FFTW兼容的数组,仅初始化为单精度
            mfout = new fftwf_complexarray(fout);
            mdin = new fftw_complexarray(din);
            mdout = new fftw_complexarray(dout);

            Complex<->Complex transforms
            mplan1 = fftwf_plan.dft_1d(n, mfin, mfout, fftw_direction.Forward, fftw_flags.Estimate);
            mplan2 = fftwf_plan.dft_1d(n, mfin, mfout, fftw_direction.Forward, fftw_flags.Measure);
            mplan3 = fftwf_plan.dft_1d(n, mfin, mfout, fftw_direction.Forward, fftw_flags.Patient);

            mplan4 = fftwf_plan.dft_1d(n, mfout, mfin, fftw_direction.Backward, fftw_flags.Measure);
            
            mplan5 = fftw_plan.dft_1d(n, mdin, mdout, fftw_direction.Forward, fftw_flags.Measure);


            // fill our arrays with an arbitrary complex sawtooth-like 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;
            for (int i = 0; i < n * 2; i++) 
                din[i] = i % 50;

            //for(int i = 0; i < n * 2; i++)
            //{
             //   Console.WriteLine(fin[i]);
            //}

            // copy managed arrays to unmanaged arrays
            //将托管数组复制到非托管数组即本地执行
            // 参数:
            //   source:
            //     从中进行复制的一维数组。
            //
            //   startIndex:
            //     源数组中从零开始的索引,在此处开始复制。
            //
            //   destination:
            //     要复制到的内存指针。
            //
            //   length:
            //     要复制的数组元素的数目,从fin复制到pin。
            Marshal.Copy(fin, 0, pin, n * 2);       

            Marshal.Copy(fout, 0, pout, n * 2);

            Console.WriteLine();
        }

        public void TestAll()
        {
            TestPlan(fplan1, "single (malloc) | pin,  pout,  Forward,  Estimate");
            TestPlan(fplan2, "single (pinned) | hin,  hout,  Forward,  Estimate");
            TestPlan(fplan3, "single (pinned) | hout, pin,   Backward, Measure ");

            // set fin to 0, and try to refill it from a backwards fft from fout (aka hin/hout)
            for (int i = 0; i < fftLength * 2; i++) fin[i] = 0;

            TestPlan(fplan4, "single (pinned) | hout, hin,   Backward, Estimate");

            // check and see how we did, don't say anyt
            for (int i = 0; i < fftLength * 2; i++)
            {
                // check against original values
                // note that we need to scale down by length, due to FFTW scaling by N
                if (System.Math.Abs(fin[i]/fftLength - (i % 50)) > 1e-3)
                {
                    System.Console.WriteLine("FFTW consistency error!");
                    return;
                }
            }

            TestPlan(fplan5, "double (pinned) | hdin, hdout, Backward, Measure ");


            Console.WriteLine();
            TestPlan(mplan1, "#1 single (managed) | mfin, mfout, Forward,  Estimate");
            TestPlan(mplan2, "#2 single (managed) | mfin, mfout, Forward,  Measure ");
            TestPlan(mplan3, "#3 single (managed) | mfin, mfout, Forward,  Patient ");
            Console.WriteLine();

            // fill our input array with an arbitrary complex sawtooth-like signal
            for (int i = 0; i < fftLength * 2; i++) fin[i] = i % 50;
            for (int i = 0; i < fftLength * 2; i++) fout[i] = 0;

            mfin.SetData(fin);
            mfout.SetData(fout);
            TestPlan(mplan2, "#2 single (managed) | mfin, mfout, Forward,  Measure ");

            fout = mfout.GetData_Float();  // let's see what's in mfout
                                           // at this point mfout contains the FFT'd mfin

            TestPlan(mplan4, "#4 single (managed) | mfout, mfin, Backward, Measure ");
            // at this point we have transfarred backwards the mfout into mfin, so mfin should be very close to the original complex sawtooth-like signal

            fin = mfin.GetData_Float();
            for (int i = 0; i < fftLength * 2; i++) fin[i] /= fftLength;
            // at this point fin should be very close to the original (sawtooth-like) signal

            // check and see how we did, don't say anyt
            for (int i = 0; i < fftLength * 2; i++)
            {
                // check against original values
                if (System.Math.Abs(fin[i] - (i % 50)) > 1e-3)
                {
                    System.Console.WriteLine("FFTW consistency error!");
                    return;
                }
            }

            Console.WriteLine();

            TestPlan(mplan2, "#2 single (managed) | mfin, mfout, Forward,  Measure ");
            TestPlan(mplan5, "#5 double (managed) | mdin, mdout, Forward,  Measure ");
            Console.WriteLine();
        }

        // Tests a single plan, displaying results
        // plan: Pointer to plan to test
        public void TestPlan(object plan, string planName)
        //  fplan1, "single (malloc) | pin,  pout,  Forward,  Estimate
        {
            // a: adds, b: muls, c: fmas
            double a = 0, b = 0, c = 0;

            int start = System.Environment.TickCount;  //plan的开始时间

            if (plan is IntPtr)
            {
                IntPtr umplan = (IntPtr)plan;       //这里为什么要强转一下

                for (int i = 0; i < repeatPlan; i++)
                {
                    fftwf.execute(umplan);  //为什么不能把IntPtr直接传进去
                    
                    Console.WriteLine(Marshal.ReadIntPtr(pin));
                    //Console.WriteLine();
                    //Console.WriteLine(Marshal.ReadIntPtr(pout));
                }

               // Console.WriteLine(Marshal.ReadIntPtr(pin));
                //Console.WriteLine(Marshal.ReadIntPtr(pout));
                /// <summary>
                /// Returns (approximately) the number of flops used by a certain plan
                /// </summary>
                /// <param name="plan">The plan to measure</param>
                /// <param name="add">Reference to double to hold number of adds</param> 引用双精度保存加法数
                /// <param name="mul">Reference to double to hold number of muls</param>
                /// <param name="fma">Reference to double to hold number of fmas (fused multiply-add)</param>
                /// <remarks>Total flops ~= add+mul+2*fma or add+mul+fma if fma is supported</remarks>
                fftwf.flops(umplan, ref a, ref b, ref c);
            }

            if (plan is fftw_plan)
            {
                fftw_plan mplan = (fftw_plan)plan;

                for (int i = 0; i < repeatPlan; i++)
                {
                    mplan.Execute();
                }

                fftw.flops(mplan.Handle, ref a, ref b, ref c);
            }

            if (plan is fftwf_plan)             //这个if跟上边的if有什么区别
            {
                fftwf_plan mplan = (fftwf_plan)plan;

                for (int i = 0; i < repeatPlan; i++)
                {
                    mplan.Execute();
                }

                fftwf.flops(mplan.Handle, ref a, ref b, ref c);
            }

            double mflops = (((a + b + 2 * c)) * repeatPlan) / (1024 * 1024);   //这个评价指标是浮点运算次数,但是为什么这么算
            long ticks = (System.Environment.TickCount - start);  //plan所花费的时间

            //Console.WriteLine($"Plan '{planName}': {ticks.ToString("#,0")} us | mflops: {FormatNumber(mflops)} | mflops/s: {(1000*mflops/ticks).ToString("#,0.0")}");
            Console.WriteLine("Plan '{0}': {1,8:N0} us | mflops: {2,8:N0} | mflops/s: {3,8:N0}", planName, ticks, mflops, (1000 * mflops / ticks));
            //Console.WriteLine();

            ///pin是IntPtr类型的,理解为指向数组的指针,这样的数组怎么遍历
            /*
            for(int i = 0; i < 5; i++)
            {
                // Console.WriteLine(pin);
                //这里应该用Marshal的某个方法读取IntPtr的数,类似Marshal.readInt16(Pin),是不是这个方法还得查
                //Marshal.ReadIntPtr(pin);
                Console.WriteLine(Marshal.ReadIntPtr(pin));
                Console.WriteLine(Marshal.ReadIntPtr(pout));
            }
            */
            //Console.WriteLine();
            //Console.WriteLine(pout);
        }

        // Releases all memory used by FFTW/C#      析构函数
        ~FFTWtest()
        {
            fftwf.export_wisdom_to_filename("wisdom.wsd");

            // it is essential that you call these after finishing
            // that's why they're in the destructor. See Also: RAII
            fftwf.free(pin);
            fftwf.free(pout);
            fftwf.destroy_plan(fplan1);
            fftwf.destroy_plan(fplan2);
            fftwf.destroy_plan(fplan3);
            fftwf.destroy_plan(fplan4);
            fftwf.destroy_plan(fplan5);
            hin.Free();
            hout.Free();
        }

        static void Main(string[] args)
        {
            if (forgetWisdom)
            {
                fftwf.fftwf_forget_wisdom();
            }
            else
            {
                Console.WriteLine("Importing wisdom (wisdom speeds up the plan creation process, if that plan was previously created at least once)");
                fftwf.import_wisdom_from_filename("wisdom.wsd");
            }

            // initialize our FFTW test class
            FFTWtest test = new FFTWtest(sampleSize); //调用有参构造函数

            // run the tests, print debug output
            test.TestAll();

            // pause for user input, then quit
            System.Console.WriteLine("\nDone. Press any key to exit.");
            String str = System.Console.ReadLine();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值