C++实现二维FFT的过程
如果着急的话可以直接跳转到最后,有完整的测试代码,想要仔细了解的话可以从头开始看,包括了理论与实践,只要环境配置正确,绝对能够直接运行。
本文的前面第1、2两个部分内容来自英特尔oneAPI黑客松大赛官方提供的基础学习文档,其他相似材料主要以英特尔oneAPI黑客松大赛官方版本为准。
1 基础概念
1.1 傅里叶变换

1.2 离散傅里叶变换

1.3 快速傅里叶变换
快速傅里叶变换(Fast Fourier Transform, FFT),是快速计算序列的离散傅里叶变换(DFT)或其逆变换的方法。FFT 能够将计算DFT 的复杂度从o(n^2) 降低到 o( 𝑛𝑙𝑜𝑔(𝑛)),其中𝑛 为数据大小。



一维快速傅里叶变换的一种基础实现方式的伪代码如下所示:

1.4 实数的一维离散傅里叶变换


1.5 二维离散傅里叶变换

1.6 实数的二维离散傅里叶变换
就像实数的一维离散傅里叶变换一样,实数的二维离散傅里叶变换结果也会呈现出一种特殊的共轭对称性,one MKL FFT 库和FFTW 也利用了这一特性来节约输出序列所占的空间。

上图展示了当𝑁=8 时,随机实数矩阵进行二维离散傅里叶变换后所得到的结果。图右结果矩阵中,实心背景部分与相同颜色虚线背景部分使用了箭头指示,可以看出相对应的部分呈现对称共轭关系。
2 使用oneAPI计算FFT
2.1 oneAPI介绍
oneAPI是一个开源规范,旨在简化希望创建基于加速器的应用程序和希望支持各种硬件架构和硬件供应商的开发人员的生活。
oneAPI Base Toolkits 和H PC Toolkits 的内容如下图所示:

2.2 oneMKL 下载安装
英特尔® oneAPI 数学内核库(Intel® oneAPI Math Kernel LibraryLibrary,简称oneMKL)是一个计算数学库,其中包含高度优化的广泛线程例程,适用于需要最高性能的应用程序。该库提供Fortran 和C 编程语言接口。oneMKL C 语言接口可以从用C 或C++ 以及可以引用C 接口的任何其他语言编写的应用程序中调用。
oneMKL在这些主要计算领域提供全面的功能支持:
- BLAS(1 级、2 级和3 级)和LAPACK 线性代数例程,提供向量、向量矩阵和矩阵矩阵运算。
- ScaLAPACK 分布式处理线性代数例程,以及基本线性代数通信子程序( BLACS和并行基本线性代数子程序PBLAS)。
- oneMKL PARDISO(基于并行直接稀疏求解器PARDISO* 的直接稀疏求解器),迭代稀疏求解器,支持用于求解稀疏方程组的稀疏BLASBLAS(1 、2 和3 级)例程,以及分布式版本提供oneMKL PARDISO 求解器用于集群。
- 一维、二维或三维中的快速傅立叶变换( FFT)函数,支持混合基数(不限于2 的幂的大小),以及提供在集群上使用的这些函数的分布式版本。
- 用于优化向量数学运算的向量数学(VM) 例程。
- 矢量统计(VS) 例程,为多种概率分布、卷积和相关例程以及汇总统计函数提供高性能矢量化随机数生成器(RNG)。
- 数据拟合库,提供基于样条的函数逼近、函数导数和积分以及搜索功能。
- 扩展特征求解器,基于Feast 特征值求解器的特征求解器的共享内存编程(SMP)版本。
英特尔® oneAPI 数学核心库( oneMKL)针对英特尔处理器上的性能进行了优化。oneMKL 还可以在非Intel x86 兼容处理器上运行。
oneMKL 可以作为单独的软件包下载,
也可以作为Intel ® o neAPI Base Toolkit 的一部分下载,
进入到下载界面后,选择自己的操作系统以及在线或者离线安装方式之后,按照提示进行安装即可。



2.3 Linux离线安装示例
例如,这里我们选择了Linux 的离线安装方式,只需执行以下命令即可:
wget https://registrationcenter-
download.intel.com/akdlm/IRC_NAS/adb8a02c-4ee7-4882-97d6-
a524150da358/l_onemkl_p_2023.2.0.49497_offline.sh
sudo sh ./l_onemkl_p_2023.2.0.49497_offline.sh
安装脚本添加链接描述 的常用参数如下:

例如:
# 显示安装程序帮助:
./l_onemkl_p_2023.2.0.49497_offline.sh -a -h
# 显示可用的产品:
./l_onemkl_p_2023.2.0.49497_offline.sh -a --list-products
# 以 GUI 模式运行安装程序:
./l_onemkl_p_2023.2.0.49497_offline.sh
# 以命令行界面运行安装程序:
./l_onemkl_p_2023.2.0.49497_offline.sh -a -c
# 以静默模式运行安装程序:
./l_onemkl_p_2023.2.0.49497_offline.sh -a -s --eula accept
2.4 one MKL 随机数生成器
oneMKL 随机数生成器的典型流程如下:
- 创建和初始化流。函数vslNewStream, vslNewStreamEx, vslCopyStream, vslCopyStreamState,
vslLeapfrogStream, vslSkipAheadStream, vslSkipAheadStreamEx 。 - 调用一个或多个随机数生成器。
- 处理输出。
- 使用vslDeleteStream 函数删除一个或多个流。
生成单精度随机数的示例代码如下:
#include <stdio.h>
#include "mkl_vsl.h"
float r[1000]; /* buffer for random numbers */
VSLStreamStatePtr stream;
/* Initializing */
vslNewStream(&stream, VSL_BRNG_MT19937, 777);
/* Generating */
vsRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream, 1000, r, 0.0, 1.0);
/* Deleting the stream */
vslDeleteStream(&stream);
2.5 one MKL FFT 函数
one MKL 提供了一个接口,用于通过快速傅里叶变换算法计算离散傅里叶变换。函数名称中的前缀Dfti 和配置参数名称中的前缀 DFTI 代表离散傅立叶变换接口。
one MKL 中提供的快速傅里叶变换函数有以下2 种实现:
- 适用于单处理器或共享内存系统的FFT 函数
- 适用于分布式内存架构的Cluster FFT 函数(仅适用于 Intel® 64 架构)
FFT 和 Cluster FFT 函数均通过五个步骤计算 FFT:
- 通过调用DftiCreateDescriptor 或DftiCreateDescriptorDM 函数为问题分配新的描述符。描述符记录变换的配置,例如维度(或等级)、大小、变换数量、输入/输出数据的内存布局(由步幅定义)和缩放因子。许多配置设置都在此调用中分配了默认值,您可能需要在应用程序中修改这些默认值。
- (可选)根据需要调用DftiSetValue 或DftiSetValueDM 函数来调整描述符配置。通常,您必须仔细定义 FFT 的数据存储布局或集群 FFT 进程之间的数据分布。描述符的配置设置(例如默认值)可以使用DftiGetValue 或DftiGetValueDM 函数获取。
- 通过调用DftiCommitDescriptor 或DftiCommitDescriptorDM 函数来提交描述符,也就是说,使描述符为变换计算做好准备。一旦描述符被提交,变换的参数,例如变换的类型和数量、步幅和距离、数据的类型和存储布局等等,就被“冻结”在描述符中。
- 根据需要多次调用DftiComputeForward / DftiComputeBackward 或DftiComputeForwardDM / DftiComputeBackwardDM 函数来计算变换。由于描述符是单独定义和提交的,因此计算函数所做的就是获取输入和输出数据并按照定义计算变换。要修改对计算函数的另一次调用的任何配置参数,请使用DftiSetValue 后跟DftiCommitDescriptor(DftiSetValueDM 后跟DftiCommitDescriptorDM)或创建并提交另一个描述符。
- 通过调用DftiFreeDescriptor 或DftiFreeDescriptorDM 函数来释放描述符。这会将描述符内部消耗的内存返回给操作系统。
所有上述函数都返回一个整数状态值,成功完成操作后该值为零。您可以借助DftiErrorClass 或DftiErrorMessage 函数解释非零状态。
使用oneMKL FFT 实现单精度二维的复数到复数和实数到复数原地快速傅里叶变换的代码示例如下:
/* C99 example */
#include "mkl_dfti.h"
/* complex data in, complex data out */
float _Complex c2c_data[32][100];
/* real data in, complex data out */
float r2c_data[34][102];
DFTI_DESCRIPTOR_HANDLE my_desc1_handle = NULL;
DFTI_DESCRIPTOR_HANDLE my_desc2_handle = NULL;
MKL_LONG status;MKL_LONG dim_sizes[2] = {
32, 100};
/* ...put values into c2c_data[i][j] 0<=i<=31, 0<=j<=99 */
/* ...put values into r2c_data[i][j] 0<=i<=31, 0<=j<=99 */ status =
DftiCreateDescriptor(&my_desc1_handle, DFTI_SINGLE, DFTI_COMPLEX, 2, dim_sizes);
status = DftiCommitDescriptor(my_desc1_handle);
status = DftiComputeForward(my_desc1_handle, c2c_data); status =
DftiFreeDescriptor(&my_desc1_handle);
/* result is the complex value c2c_data[i][j], 0<=i<=31, 0<=j<=99 */ status =
DftiCreateDescriptor(&my_desc2_handle

本文介绍了如何使用Intel oneAPI中的oneMKL库计算二维快速傅里叶变换(FFT),包括oneAPI的基本概念、oneMKL的下载安装、随机数生成和FFT函数的使用。通过C++代码示例展示了oneMKL与开源FFTW的性能对比,验证了计算结果的正确性。
1万+

被折叠的 条评论
为什么被折叠?



