目录
前言
1、大赛要求
本次比赛为命题形式,题目要求如下:
- 使用oneMKl工具生成2048*2048随机单精度实数
- 使用FFT算法实现实数到复数的快速傅里叶变换
- 使用oneMKL加速计算实数到复数的快速傅里叶变换
- 对比上面两种快速傅里叶变换的精度、性能
2、oneMKL介绍
oneMKL(oneAPI Math Kernel Library)是oneAPI包含的一种数学工具,能对各种数据工程问题实现加速与优化。
oneAPI官网:oneAPI
oneMKL官网:oneMKL
oenMKL对C语言的API文档:oneMKL—C语言参考文档
3、准备
- Ubuntu系统
- C++基础
- oneMKL
由于题目比较简单,不需要很复杂的编译方法,上手简单。使用onelMKL工具的话,你的电脑的CPU最好是intel的。而且推荐使用linux系统,加速效果明显。
oneMKL基本使用
oneMKL下载网址:oneMKL下载
注意要使用离线版本安装,如果你使用的是为window的话,使用在线版本的方式安装。
1、下载:
一定要注意下载的是oneMKl_baseKit,不能仅仅下载MKL工具包
wget https://registrationcenter-download.intel.com/akdlm/IRC_NAS/992857b9-624c-45de-9701-f6445d845359/l_BaseKit_p_2023.2.0.49397_offline.sh
2、安装:
sudo sh ./l_BaseKit_p_2023.2.0.49397_offline.sh
默认安装目录:/opt/intel/oneapi
3、初始化oneMKL环境:
source /opt/intel/oneapi/setvars.sh
可以将这条命令放到~/bashrc文件内并激活,这样不用每次启动一个终端都初始化了。
4、编译代码
icpx -qmkl my.cpp -o my.out
注意其中的-qmkl是比较方便的也是不容易出错的动态库链接参数,它是把关于oneMKL的所有动态库都链上了,懒人必备。
5、运行
./my.out
直接把编译好的运行就可。
所需的头文件
#include <chrono> // 计算程序运行时间
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <mkl.h> // onemkl工具包
#include "fftw3.h" // fftw3,onemkl自带,使用fftw3来对比经过oneMKL加速过的fft
using namespace std;
使用oneMKL工具生成随机数
#define ROW 2048
#define COL 2048
float *data = (float *)malloc((ROW * COL) * sizeof(float));
VSLStreamStatePtr stream;
vslNewStream(&stream, VSL_BRNG_MT19937, 42); // 42是随机数种子
vsRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream, ROW * COL, data, 0.0f, 1.0f); // 生成0到1之间的随机数
vslDeleteStream(&stream);
使用fftw3计算FFT
void fftw3(float *data, MKL_Complex8 *x)
{
fftwf_plan r2c;
r2c = fftwf_plan_dft_r2c_2d(ROW, COL, data, (fftwf_complex *)x, FFTW_ESTIMATE);
fftwf_execute(r2c);
fftwf_destroy_plan(r2c);
}
// 开辟内存空间,存储普通FFT计算的结果
MKL_Complex8 *x = (MKL_Complex8 *)malloc(ROW * (COL / 2 + 1) * 2 * sizeof(float));
fftw3(data, x);
注意给x开的空间是:ROW * (COL / 2 + 1) * 2 * sizeof(float)
,
调用oneMKL API加速计算FFT
void r2c_oneMKL(float *data, MKL_Complex8 *y)
{
MKL_LONG status;
MKL_LONG dim_sizes[2] = {ROW, COL};
DFTI_DESCRIPTOR_HANDLE handle;
status = DftiCreateDescriptor(&handle, DFTI_SINGLE, DFTI_REAL, 2, dim_sizes);
status = DftiSetValue(handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE); // 不覆盖data
status = DftiSetValue(handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);
status = DftiCommitDescriptor(handle);
status = DftiComputeForward(handle, data, y);
DftiFreeDescriptor(&handle);
}
// 开辟空间,存储oneMKL API FFT计算的结果
MKL_Complex8 *y = (MKL_Complex8 *)malloc(ROW * COL * 2 * sizeof(float));
r2c_oneMKL(data, y);
注意,要多给y多开点内存空间,乘以2是因为有实部和虚部
对比两种方法的准确性
对比的时候要对比实部和虚部
void compare_results(MKL_Complex8 *x, MKL_Complex8 *y)
{
bool is_same=true;
// 实部对比
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < (COL / 2 + 1); j++)
{
// cout << x[i*(ROW/2+1)+j].real<< " ";
// cout << y[i*(COL)+j].real<< " ";
// 实部一个一个比较:
if (x[i * (COL / 2 + 1) + j].real - y[i * (COL) + j].real > 1e-6)
{
is_same=false;
break;
}
}
}
if (is_same){
cout<<"实部:"<<"结果正确"<<endl;
}else{
cout<<"实部:"<<"结果不正确"<<endl;
}
// 虚部对比
is_same=true;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < (COL / 2 + 1); j++)
{
// 虚部一个一个比较:
if (x[i * (COL / 2 + 1) + j].imag - y[i * (COL) + j].imag > 1e-6)
{
is_same=false;
break;
}
}
}
if (is_same){
cout<<"虚部:"<<"结果正确"<<endl;
}else{
cout<<"虚部:"<<"结果不正确"<<endl;
}
}
输出结果
结束语
本次大赛的题目比较基础,对非计算机专业的工科生很友好。本人是地质专业,由于要经常进行地震数据处理、地震数据解释等,对傅里叶变换的需要也很大,oneMKl工具计算速度快,对大型地震数据的复杂计算有着不可替代的性能优势。
其他
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2bo5wurw8qjow