FFTW3在VS2013下的安装与使用示例

来源:互联网 发布:mars java视频教程 编辑:程序博客网 时间:2024/06/05 22:47

1.下载与安装

安装的过程花费了我大量的时间,最终在这篇博客《Windows下FFTW库的安装》 中找到了合适方法,经过简单的修改,终于将其安装成功了。

1.1下载

FFTW是用来计算一维或者多维的离散傅里叶变换,输入可以为实数序列也可以为复数序列的C语言的子函数库,FFTW是免费软件,是作为fft函数库的各种应用的上佳选择。

  1. 从网站http://www.fftw.org/install/windows.html上下载最新的预编译文件:

    • 32-bit version: fftw-3.3.5-dll32.zip (2.6MB)[虽然我的系统是64位的,但安装成功的是这个]
    • 64-bit version: fftw-3.3.5-dll64.zip (3.1MB)

1.2安装

  1. 安装lib.exe, 其实这是VC、VS系列自带的工具,在VC6.0和VS2013中都有的,我机器上安装了VC6.及VS2013 ,lib.exe的路径如下:
    D:\Program Files\Microsoft Visual Studio\VC98\Bin (VC6.0的目录)
    D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin (VS2013的目录)

  2. 打开Windows的命令提示行窗口(CMD窗口),改变当前目录到D:\Program Files\Microsoft Visual Studio 12\VC\bin(使用cd命令)
    这里写图片描述

  3. 把下载的文件夹中的libfftw3-3.def,libfftw3f-3.def,libfftw3l-3.def也拷到bin文件夹中。运行下列代码。注意这里默认指定machine为X86架构,所以可能和网上其他的代码有些不一样。
    lib /def:libfftw3-3.def
    lib /def:libfftw3f-3.def
    lib /def:libfftw3l-3.def
  4. 将libfftw3f-3.dll libfftw3-3.dll libfftw3l-3.dll放入windows/system32中,然后将生成的库文件libfftw3-3.lib libfftw3f-3.lib libfftw3l-3.lib放入VS中的lib文件夹,把fftw3.h放入include文件夹。
    这里写图片描述
  5. 在新建工程的时候,记得#include”fftw3.h“,然后设置参数:”项目“==>”项目属性”==>”配置属性”==>”链接器”==>”输入”==>”附加依赖项”,将以下三项添加进去:[这一步很重要!!!]
    libfftw3-3.lib
    libfftw3f-3.lib
    libfftw3l-3.lib
    这里写图片描述

2.测试与使用示例

2.1代码测试

下面是一段测试代码:

#include "fftw3.h"  int main()  {      fftw_complex *in, *out;      fftw_plan p;      int N= 8;      int i;      int j;      in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);      out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);      for( i=0; i < N; i++)      {          in[i][0] = 1.0;          in[i][1] = 0.0;          printf("%6.2f ",in[i][0]);      }      printf("\n");      p=fftw_plan_dft_1d(N,in,out, FFTW_FORWARD, FFTW_ESTIMATE);      fftw_execute(p); /* repeat as needed*/      for(j = 0;j < N;j++)      {          printf("%6.2f ",out[j][0]);      }      printf("\n");      fftw_destroy_plan(p);      fftw_free(in);       fftw_free(out);      system("pause");//暂停    return 0;  } 

2.2代码使用示例

使用 FFTW 编程的结构如下:

#include <fftw3.h>...{fftw_complex *in, *out;fftw_plan p;...in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);// 输入数据in 赋值p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD,FFTW_ESTIMATE);fftw_execute(p); // 执行变换...fftw_destroy_plan(p);fftw_free(in);fftw_free(out);}

上述代码的流程为:

Created with Raphaël 2.1.0开始为输入输出数据申请内存构造策略plan执行策略plan销毁策略(如果不复用的话)释放输入输出数据的内存结束
  1. 内存空间的申请与释放
    以 in 的分配为例。
    在这里,空间分配有三种可以选择的方式:
    第一, 直接 in[N];
    第二, 使用 ANSI C 或者 C++语言中的 malloc, new 等动态分配;
    第三, 使用 FFTW 提供的 fftw_malloc 函数动态分配。
    第一种方法的问题在于,这种方法申请的空间由编译器在栈内存里分配,众所周知,栈内存是非常有限的,在 windows 上为 2M 大小,所以对于大的数据容易导致 Stack Overflow的致命性错误。当然,你可以在编译器设置里边它调大一些,但是换了个环境,你是不是就愣了~
    第二种方法虽然解决了第一种方法存在的问题,但是,由于不同的编译器内存分配策略的不同,可能使分配的数组并没有内存对齐,而内存没有对齐,对 FFTW 性能上的影响将是显著的。当然,你可以使用一些语言的特性让内存对齐,但是,有下面更好的方法,为什么非要用这种方法呢~
    第三种方法则是由 FFTW 提供的内存分配接口,它在堆内存上申请空间,并且能够保证内存对齐,同时可以在不同的平台之间顺利的移植,何乐而不为~
    空间的分配问题解决了,空间的释放问题当然也就水到渠成了,如果我们使用了fftw_malloc()来分配空间,那么使用 fftw_free()释放相应的空间就行了。
    最后,在内存方面值得注意的问题是, fftw_plan 是一种声明了一个变量就需要使用fftw_destroy_plan()来销毁的类型。
  2. 实数 FFT 中内存的复用
    对于实数输入的序列,其 DFT 输出是一个有共轭对称性质的复数序列,在 FFTW 中,两
    位博士利用了这一点来减少内存空间的使用。[这一部分以后再研究]。
  3. FFTW 为不同的输入数据、不同的变换类型[DFT、IDFT]提供了不同的策略plan
fftw_plan fftw_plan_dft_1d(int n, fftw_complex *in,fftw_complex *out,int sign, unsigned flags);//一维复数据的DFT、 IDFTfftw_plan fftw_plan_dft_2d(int n0, int n1,fftw_complex *in, fftw_complex *out,int sign, unsigned flags);//二维复数据DFT、 IDFTfftw_plan fftw_plan_dft_3d(int n0, int n1, int n2,fftw_complex *in, fftw_complex *out,int sign, unsignedflags);//三维复数据DFT、 IDFTfftw_plan fftw_plan_dft(int rank, const int *n,fftw_complex *in, fftw_complex *out,int sign, unsigned flags);//rank维复数据DFT、 IDFTfftw_plan fftw_plan_dft_r2c_1d(int n, double *in,fftw_complex *out,unsigned flags);//一维实数DFT,注意这里没有sign参数,只能做正变换fftw_plan fftw_plan_dft_c2r_1d(int n, fftw_complex *in,double *out,unsigned flags);//一维实数IDFT,只能做逆变换fftw_plan fftw_plan_dft_r2c_2d(int n0, int n1,double *in,fftw_complex *out,unsigned flags);//二维实数DFT,只能做正变换fftw_plan fftw_plan_dft_r2c_3d(int n0, int n1, int n2,double *in, fftw_complex *out,unsigned flags);//三维实数DFT,只能做正变换fftw_plan fftw_plan_dft_r2c(int rank, const int *n,double *in, fftw_complex *out,unsigned flags);//rank维实数DFT,只能做正变换

【参数介绍】
第一个参数n是进行变换计算的数据的长度,其值必须是正整数;
第二、第三个参数是输入输出数组的指针;
第四个参数int sign只在复数据的 DFT 中, sign 表示了是做 DFT 变换(FFTW_FORWARD)还是 IDFT 变换(FFTW_BACKWARD)。这里是默认的宏定义,#define FFTW_FORWARD (-1)和#define FFTW_BACKWARD (+1)
第五个参数 flags,是我们要重点讨论的策略生成方案。
flags 参数一般情况下常用的为 FFTW_MEASUREFFTW_ESTIMATEFFTW_MEASURE表示 FFTW 会先计算一些 FFT 并测量所用的时间,以便为大小为 n 的变换寻找最优的计算方法。依据机器配置和变换的大小( n), 这个过程耗费约数秒(时钟 clock 精度)。FFTW_ESTIMATE 则相反,它直接构造一个合理的但可能是次最优的方案。总体来说,如果你的程序需要进行大量相同大小的 FFT,并且初始化时间不重要,可以使用 FFTW_MEASURE,否则应使用 FFTW_ESTIMATEFFTW_MEASURE 模式下 in 和 out 数组中的值会被覆盖,所以该方式应该在用户初始化输入数据 in 之前完成。
4. 对 plan 的重用
从上面的测试中我们看到,生成一个 plan 有多么不容易,多么费时间,那么,我们可
不可以生成一个 plan,然后在其他的情况下重用这个 plan 呢?答案当然是肯定的。
当然,重用也是有条件的:

  • 输入输出数据的大小相等。
  • 输入输出的数据对齐不变。按照前面所说,都是用 fftw_malloc()就不会有问题了。
  • 变换类型、是否原位变换不变。

具体函数接口如下所列。

void fftw_execute_dft(const fftw_plan p,fftw_complex *in,fftw_complex *out);void fftw_execute_dft_r2c(const fftw_plan p,double *in,fftw_complex *out);void fftw_execute_dft_c2r(const fftw_plan p,fftw_complex *in, double *out);

5.接触 wisdom
5.1 wisdom 是什么
在上面的测试中,我们已经发现,生成一个策略有多么不容易,多么费时间,那么,除
了重用 plan 这样的方法能够减少生成策略的次数,还有没有别的办法来减少生成策略所花费的时间呢?答案当然也是肯定的。这就是我们这里需要介绍的 wisdom。
wisdom 的大体思路就是把生成好的策略相关的配置信息存储在磁盘里,然后在下次重
新运行程序的时候,把策略相关的配置信息重新载入到内存中,这样在重新生成 plan 的时候就可以节约大量的时间。
5.2 如何使用 wisdom
由于 FFTW起初是在 linux环境下开发的,当我们需要在 windows下使用 wisdom的时候,我们需要在头文件 fftw3.h 中添加以下代码。

static void my_fftw_write_char(char c, void *f){ fputc(c, (FILE *)f);}#define fftw_export_wisdom_to_file(f) fftw_export_wisdom(my_fftw_write_char, (void*)(f))static int my_fftw_read_char(void *f){ return fgetc((FILE *)f);}#define fftw_import_wisdom_from_file(f)fftw_import_wisdom(my_fftw_read_char, (void*)(f))

如果需要使用其他的精度,则添加代码的方法类似。
wisdom 存储起来的不是 plan 本身,而是和 plan 相关的配置信息,例如内存、寄存器等。所以我们在把 wisdom 载入到内存中后,我们还是需要调用生成 plan 的函数的。使用 wisdom的基本代码框架如下所示。

/* 导出wisdom */fftw_complex *in = NULL, *out = NULL;fftw_plan p;in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)* row * col);out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)* row * col);p = fftw_plan_dft_2d(row, col,in, out, FFTW_FORWARD,FFTW_MEASURE);FILE *fp = fopen("fftw_plan_dft_2d.txt", "w");fftw_export_wisdom_to_file(fp);fclose(fp);fftw_destroy_plan(p);fftw_free(in);fftw_free(out);
/* 导入wisdom */Fftw_comlex *in = NULL, *out = NULL;fftw_plan p;in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)* row * col);out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)* row * col);FILE *fp = fopen("fftw_plan_dft_2d.txt", "r");fftw_import_wisdom_from_file(fp);fclose(fp);p = fftw_plan_dft_2d(row, col,in, out, FFTW_FORWARD,FFTW_MEASURE);for(int i=0; i<row*col; i++){in[i][0] = (double)i*i + 1;in[i][1] = (double)i/3;}fftw_execute(p);fftw_destroy_plan(p);fftw_free(in);fftw_free(out);

5.3 在使用 wisdom 的时候需要尤其注意的几点
 不管是 fftw_import_wisdom_from_file(FILE *fp)还是 fftw_export_wisdom_to_file(FILE *fp),里面的文件指针 fp 在调用函数前应该是打开的,在调用函数之后也是打开的,需要我们调用函数将它关闭。

  • 每一个 wisdom 都是针对某一个确定的 processor 而言的。每次更换了运行的硬件环境,都应该重新生成 wisdom。
  • 每一个 wisdom 都是针对某一个确定的程序而言的。每次修改了程序,即最后的二进制文件不同,都需要重新生成 wisdom。如果不重新生成 wisdom,相对于硬件环境的改变引起的效率下降,这里的效率下降没有那么明显。
  • 对相同的 processor 和相同的 program binary,在每次运行的时候,也会因为虚拟内存使用的不同而导致程序执行效率的改变。这一条对于性能要求特别严格的情况,开发者需要考虑。

参考文献

  1. 《FFTW 使用手册》.熊金水.xjs.xjtu@gmail.com.2011-5-11
  2. 《FFTW》for version 3.3.5 . Matteo Frigo ,Steven G. Johnson. 30 July 2016
  3. Windows下FFTW库的安装 - moyumoyu的专栏:
    http://blog.csdn.net/moyumoyu/article/details/7950528
0 0