您的位置:首页 > 编程语言 > C语言/C++

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

2016-10-26 18:29 453 查看

1.下载与安装

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

1.1下载

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

从网站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安装

安装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的目录)

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



把下载的文件夹中的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

将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文件夹。



在新建工程的时候,记得#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销毁策略(如果不复用的话)释放输入输出数据的内存结束

内存空间的申请与释放

以 in 的分配为例。

在这里,空间分配有三种可以选择的方式:

第一, 直接 in


第二, 使用 ANSI C 或者 C++语言中的 malloc, new 等动态分配;

第三, 使用 FFTW 提供的 fftw_malloc 函数动态分配。

第一种方法的问题在于,这种方法申请的空间由编译器在栈内存里分配,众所周知,栈内存是非常有限的,在 windows 上为 2M 大小,所以对于大的数据容易导致 Stack Overflow的致命性错误。当然,你可以在编译器设置里边它调大一些,但是换了个环境,你是不是就愣了~

第二种方法虽然解决了第一种方法存在的问题,但是,由于不同的编译器内存分配策略的不同,可能使分配的数组并没有内存对齐,而内存没有对齐,对 FFTW 性能上的影响将是显著的。当然,你可以使用一些语言的特性让内存对齐,但是,有下面更好的方法,为什么非要用这种方法呢~

第三种方法则是由 FFTW 提供的内存分配接口,它在堆内存上申请空间,并且能够保证内存对齐,同时可以在不同的平台之间顺利的移植,何乐而不为~

空间的分配问题解决了,空间的释放问题当然也就水到渠成了,如果我们使用了fftw_malloc()来分配空间,那么使用 fftw_free()释放相应的空间就行了。

最后,在内存方面值得注意的问题是, fftw_plan 是一种声明了一个变量就需要使用fftw_destroy_plan()来销毁的类型。

实数 FFT 中内存的复用

对于实数输入的序列,其 DFT 输出是一个有共轭对称性质的复数序列,在 FFTW 中,两

位博士利用了这一点来减少内存空间的使用。[这一部分以后再研究]。

FFTW 为不同的输入数据、不同的变换类型[DFT、IDFT]提供了不同的策略plan

fftw_plan fftw_plan_dft_1d(int n, fftw_complex *in,fftw_complex *out,int sign, unsigned flags);//一维复数据的DFT、 IDFT

fftw_plan fftw_plan_dft_2d(int n0, int n1,fftw_complex *in, fftw_complex *out,int sign, unsigned flags);//二维复数据DFT、 IDFT

fftw_plan fftw_plan_dft_3d(int n0, int n1, int n2,fftw_complex *in, fftw_complex *out,int sign, unsigned
flags);//三维复数据DFT、 IDFT

fftw_plan fftw_plan_dft(int rank, const int *n,fftw_complex *in, fftw_complex *out,int sign, unsigned flags);//rank维复数据DFT、 IDFT

fftw_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,在每次运行的时候,也会因为虚拟内存使用的不同而导致程序执行效率的改变。这一条对于性能要求特别严格的情况,开发者需要考虑。

参考文献

《FFTW 使用手册》.熊金水.xjs.xjtu@gmail.com.2011-5-11

《FFTW》for version 3.3.5 . Matteo Frigo ,Steven G. Johnson. 30 July 2016

Windows下FFTW库的安装 - moyumoyu的专栏:

http://blog.csdn.net/moyumoyu/article/details/7950528
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  FFTW FFT c语言