目录
引言
FFTW简介
基本信息
功能特点
交叉编译的过程
测试
测试程序
测试结果
结语
引言
FFT(快速傅里叶变换)在众多领域具有不可替代的重要意义,它能将时域信号转换为频域信号,极大提升了离散傅里叶变换(DFT)的计算效率,让信号分析、数据处理和系统建模变得更加高效、可行。而FFT性能测试的意义也十分显著,它能够量化评估FFT算法或相关软件库(如FFTW)在不同计算场景下的运算速度、精度以及资源利用效率等指标,帮助开发者了解其性能瓶颈,以便针对性地进行优化和改进。同时,也为在实际应用中选择合适的FFT实现方案提供了客观依据,确保在信号处理、科学计算等领域中能高效、准确地完成傅里叶变换任务,充分发挥FFT的价值。今天就在ELF2学习板上做一下FFTW的测试。
FFTW简介
FFTW(Fastest Fourier Transform in the West)是一个用于计算离散傅里叶变换(DFT)的开源软件库,在科学计算、信号处理、图像处理等众多领域应用广泛。以下从多个方面为你详细介绍 FFTW:
基本信息
- 开源性质:FFTW 是开源的,其源代码遵循 GPL(GNU General Public License)许可协议,这意味着开发者可以自由地使用、修改和分发该库。
- 多语言支持:它不仅提供了 C 语言接口,还有 C++、Fortran 等语言的接口,方便不同编程语言的开发者使用。
- 平台兼容性:FFTW 具有良好的跨平台性,可在多种操作系统(如 Linux、Windows、macOS 等)和硬件架构(如 x86、ARM 等)上运行。
功能特点
- 高性能:这是 FFTW 最显著的特点。它采用了自适应算法,能够根据输入数据的规模、数据类型以及目标平台的特性(如 CPU 架构、缓存大小等)自动选择最优的计算方案,从而在不同的硬件环境下都能实现高效的 DFT 计算。
- 支持多种变换类型:FFTW 支持多种类型的离散傅里叶变换,包括一维、二维和多维的 DFT,以及实数据和复数据的正变换和逆变换。此外,还支持离散余弦变换(DCT)和离散正弦变换(DST)等相关变换。
- 精度可选:提供单精度(float)和双精度(double)两种数据类型的支持,开发者可以根据实际需求选择合适的精度,在精度和计算效率之间进行权衡。
- 多线程并行计算:支持多线程并行计算,通过 OpenMP 等多线程编程模型,能够充分利用多核处理器的性能,进一步提高计算速度。
交叉编译的过程
FFTW 有三个版本的数据类型:double、float 和 long double,.默认FFTW编译生成double类型,加入参数“--enable-single”或“--enable-float”编译单精度(float),加入参数“--enable-long-double”支持长双进度类型。在ARM平台上可增加“--enable-neon”,使能ARM NEON流媒体加速核心,但是这个选项不适用AARCH64平台。
使用下面的命令进行环境配置:
./configure --prefix=/mnt/d/test/install --host=arm-linux --enable-float CC=aarch64-linux-gnu-gcc
然后进行make和make install即可。成功后在lib目录下可以看到静态库文件。
测试
测试程序
以下测试程序采用豆包生成。它会调用 FFTW 库来实现 2048 点单精度的 FFT(快速傅里叶变换)和 IFFT(逆快速傅里叶变换),同时精确测量所需时间。
#include <stdio.h>
#include <stdlib.h>
#include <fftw3.h>
#include <time.h>// 计算时间差(单位:微秒)
long long get_time_diff_us(struct timespec start, struct timespec end) {return (end.tv_sec - start.tv_sec) * 1000000LL + (end.tv_nsec - start.tv_nsec) / 1000;
}int main() {const int N = 2048;fftwf_complex *in, *out, *back;fftwf_plan p, q;struct timespec start, end;long long fft_time_us, ifft_time_us;// 分配内存in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * N);out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * N);back = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * N);// 初始化输入数据for (int i = 0; i < N; i++) {in[i][0] = (float)i; // 实部in[i][1] = 0.0f; // 虚部}// 创建FFT计划p = fftwf_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);// 记录FFT开始时间clock_gettime(CLOCK_MONOTONIC, &start);// 执行FFTfftwf_execute(p);// 记录FFT结束时间clock_gettime(CLOCK_MONOTONIC, &end);// 计算FFT时间fft_time_us = get_time_diff_us(start, end);// 创建IFFT计划q = fftwf_plan_dft_1d(N, out, back, FFTW_BACKWARD, FFTW_ESTIMATE);// 记录IFFT开始时间clock_gettime(CLOCK_MONOTONIC, &start);// 执行IFFTfftwf_execute(q);// 记录IFFT结束时间clock_gettime(CLOCK_MONOTONIC, &end);// 计算IFFT时间ifft_time_us = get_time_diff_us(start, end);// 输出结果printf("2048点单精度FFT所需时间: %lld 微秒\n", fft_time_us);printf("2048点单精度IFFT所需时间: %lld 微秒\n", ifft_time_us);// 释放计划和内存fftwf_destroy_plan(p);fftwf_destroy_plan(q);fftwf_free(in);fftwf_free(out);fftwf_free(back);return 0;
}
代码说明:
- 头文件包含:引入了必要的头文件,像
stdio.h
用于输入输出,fftw3.h
用于 FFTW 库的功能,time.h
用于时间测量。 - 时间计算函数:
get_time_diff_us
函数用来计算两个timespec
结构体之间的时间差,单位为微秒。 - 内存分配:借助
fftwf_malloc
函数为输入、输出和逆变换结果分配内存。 - 数据初始化:把输入数据的实部初始化为从 0 到 2047 的整数,虚部初始化为 0。
- FFT 计划创建与执行:使用
fftwf_plan_dft_1d
创建 FFT 和 IFFT 计划,运用fftwf_execute
执行变换。 - 时间测量:在执行 FFT 和 IFFT 前后分别调用
clock_gettime
记录时间,然后计算时间差。 - 结果输出:输出 FFT 和 IFFT 所需的时间。
- 资源释放:使用
fftwf_destroy_plan
销毁计划,用fftwf_free
释放内存。
为了提高速度,我把程序中的FFTW_ESTIMATE改为FFTW_MEASURE。
测试结果
采用如下命令编译测试程序:
aarch64-linux-gnu-gcc -o ffwtest ffwtest.c -Iinclude -Llib -lfftw3f -lm
然后将程序拷贝到开发板上进行运行。
可以看到完成一次fft大概需要110us。
结语
通过本次对 FFTW 的测试探索,我们深入了解了其在不同场景下的性能表现与优势,无论是在信号处理、科学计算,还是工程应用领域,FFTW 都展现出强大的计算能力与高效性。当然,测试过程中或许还存在一些尚未触及的细节,性能优化的空间也依然存在。希望这篇博客能为大家在使用 FFTW 时提供一些参考和启发。如果你在实践中遇到任何问题,或是有新的测试发现与优化思路,欢迎在评论区分享交流。后续我也会继续探索更多关于 FFT 的使用技巧与实践经验,期待与大家共同进步!