一、FFTW简介
FFT(快速傅里叶变换)是数字信号处理的超级经典算法,在量子物理、光谱分析、音视频流信号处理、石油勘探、地震预报、天气预报、概率论、编码理论、医学断层诊断等领域有着广泛的应用。在数学中,傅里叶级数(Fourier series)是把类似波的函数表示成简单正弦波的方式。
快速傅里叶变换的算法有许多,既有FFTW这种开源库,一些芯片企业也针对自家芯片设计FFT优化库,例如Intel的MKL_FFT,华为的KML_FFT。
打住,这里有一个问题?
既然有FFTW这种通用的、开源的还免费的库,为什么芯片公司还要费人费钱去自己搞FFT优化库呢?
通过这篇文章,也许你就会知其一二。
FFTW(Fast Fourier Transform in the West)是一款用于快速傅里叶变换(FFT)的开源库,它是自由软件许可证(类似于GPL)下发布的,您可以在其官方网站或代码仓库上找到最新版本的FFTW,然后根据许可证的规定使用它。它可以用于在多种计算平台上进行高效的数值计算。FFTW通过使用预分解算法和缓存技术,可以大大提高FFT的计算速度,与传统的FFT算法相比,可以节省数倍的计算时间。FFTW不仅可以用于科学计算和信号处理,还可以用于图像处理、音频处理等领域。FFTW的优秀性能使其成为许多科学计算和工程应用中不可或缺的工具。官网地址:http://fftw.org/ github地址:https://github.com/FFTW/fftw3
本实验仅是将FFTW搬到RISC-V硬件平台上编译和运行,文中将SG2042和FT2000进行了单核性能的对比,只是为了先建立一个优化对标,并不代表这两款芯片性能的差距。通常一些软件测试出来的性能,与以下几个因素相关:
芯片的硬件算力水平
编译器(RISC-V的GCC属于初代版本)
编译选项(尤其是是否启用SIMD加速)
操作系统和依赖库等其他因素
二、平台环境
[硬件参数]
处理器: 算能SG2042 x 1
核心数: 64核
L1 Cache: I:64KB and D:64KB
L2 Cache: 1MB/Cluster
L3 Cache: 64MB System Cache
[软件环境]
linux版本: 22.10
gcc版本: 10.2.0
三、安装流程
我们从官网上下载最新的程序包,当前最新版本为:3.3.10 下载链接:http://fftw.org/download.html
我们下载的文件为:fftw-3.3.10.tar.gz
解压:
ubuntu@perfxlab:~$ tar zxvf fftw-3.3.10.tar.gz
配置:
ubuntu@perfxlab:~/fftw-3.3.10$ ./configure --prefix=/usr/fftw3
安装:
ubuntu@perfxlab:~/fftw-3.3.10$ make install
配置环境变量:
我们安装后需要配置相关的环境变量,打开~/.bashrc, 在文件在最后面添加如下代码:
export LD_LIBRARY_PATH=/usr/fftw3/lib:$LD_LIBRARY_PATH
验证是否安装成功:
查询FFTW版本:
ubuntu@perfxlab:~/fftw-3.3.10$ fftw-wisdom --version
出现以下信息即为安装成功:
fftw-wisdom tool for FFTW version 3.3.10.
Copyright (c) 2003, 2007-14 Matteo Frigo
Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
我们写一个简单的测试代码验证是否安装成功:
#include <stdio.h>
#include <stdlib.h>
#include <fftw3.h>
int main() {
int N = 8; // 信号长度
fftw_complex *in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
fftw_complex *out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
// 初始化输入信号
for (int i = 0; i < N; i++) {
in[i][0] = i; // 实部
in[i][1] = 0; // 虚部
}
// 执行傅里叶变换
fftw_execute(plan);
// 打印结果
printf("Input Signal:n");
for (int i = 0; i < N; i++) {
printf("%f + %fin", in[i][0], in[i][1]);
}
printf("Transformed Signal:n");
for (int i = 0; i < N; i++) {
printf("%f + %fin", out[i][0], out[i][1]);
}
fftw_destroy_plan(plan);
fftw_free(in);
fftw_free(out);
return 0;
}
编译代码:
ubuntu@perfxlab:~/cheng$ gcc -o fftw_test testfftw.c -lfftw3 -lm
执行程序运行结果如下:
ubuntu@perfxlab:~/cheng$ ./fftw_test
Input Signal:
0.000000 + 0.000000i
1.000000 + 0.000000i
2.000000 + 0.000000i
3.000000 + 0.000000i
4.000000 + 0.000000i
5.000000 + 0.000000i
6.000000 + 0.000000i
7.000000 + 0.000000i
Transformed Signal:
28.000000 + 0.000000i
-4.000000 + 9.656854i
-4.000000 + 4.000000i
-4.000000 + 1.656854i
-4.000000 + 0.000000i
-4.000000 + -1.656854i
-4.000000 + -4.000000i
-4.000000 + -9.656854i
好了,结果正确,目前已经安装测试完成,可以愉快的使用了。
四、性能测试和对比
下面,我们将基于FFTW的开源项目,对SG2042进行性能评估,并和FT2000做简单对比。
测试代码如下
我们主要测试了FFTW(Fastest Fourier Transform in the West)库中的核心函数fftw_execute的性能。我们通过多次执行fftw_execute函数并取平均值来评估其性能,并将结果以GFlops(每秒十亿次浮点运算数)为单位进行了测量。in_place 、out_place代表是否使用了同一内存。
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <math.h> // 包含math.h以使用log2函数
int main() {
int signal_lengths[] = {32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}; // 不同信号长度
int num_lengths = sizeof(signal_lengths) / sizeof(signal_lengths[0]);
for (int i = 0; i < num_lengths; i++) {
int N = signal_lengths[i];
fftw_complex *in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
fftw_complex *out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
// 填充输入数据 (使用双精度浮点数)
for (int j = 0; j < N; j++) {
in[j][0] = 1.0; // 实部 (double)
in[j][1] = 0.0; // 虚部 (double)
}
double total_elapsed_time = 0.0;
int num_iterations = 20; // 每个长度的信号执行20次
// 执行双精度浮点数傅立叶变换
fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
for (int j = 0; j < num_iterations; j++) {
clock_t start_time = clock();
fftw_execute(plan);
clock_t end_time = clock();
double elapsed_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
total_elapsed_time += elapsed_time;
}
fftw_destroy_plan(plan);
double average_time = total_elapsed_time / num_iterations;
// 计算FLOPS(每秒浮点运算数)
double flops = 5.0 * N * log2(N) / average_time; // 假设FFT算法的复杂度为5*N*log2(N)
// 计算GFLOPS(每秒十亿次浮点运算数)
double gflops = flops / 1e9;
printf("平均FFTW执行时间(长度 %d): %f秒 GFLOPS : %fn", N, average_time , gflops);
fftw_free(in);
fftw_free(out);
}
return 0;
}
如果要测试float类型:则需要修改以下地方:
将fftw_complex改为fftwf_complex,将fftw_plan_dft_1d改为fftwf_plan_dft_1d。
编译选项
以下是编译double类型,如果编译float 需要用-lfftw3f。
SG2042编译选项(注意,FFTW目前没有支持risc-v的simd版本)
-march=rv64gcv0p7_zfh_xtheadc -mabi=lp64d -mtune=c920 -O3
FT2000 编译选项
-Wall -Wextra -funroll-loops -O3 -DNDEBUG -DNOPEN_HASWELL_OPT
性能图和对比
数据类型为float in_place:
数据类型为float out_place:
数据类型为 double in_place:
数据类型为 double out_place
五、简单总结
将FFTW迁移到RISC-V处理器SG2042上的实验过程还是相对简单,这得益于RISC-V软件生态的不断完善。但同时也依然存在一些问题:
1:从上面的测试结果来看,数据类型为float时, SG2042的表现相对FT2000 有比较明显的差距;数据类型为double时,随着信号长度的增加,性能差距逐渐减小,趋于接近。
2:FFTW目前没有支持RISC-V的Vector版本,仍有极大性能潜力待挖掘。我们或者等待FFTW发行版支持RISC-V;或者有人自研RISC-V FFT优化算法库。回到了文章最开始的问题“既然有FFTW这种通用的、开源的还免费的库,为什么芯片公司还要费人费钱去自己搞FFT优化库呢?”,实际上还有许多类似这样的计算库,存在类似的问题。