英特尔oneAPI黑客松 基于oneMKL的FFT加速

Windows平台环境配置过程

  1. 前往Visual Studio官网安装Visual Studio Community,安装时选择使用C++进行开发。
  2. 前往oneMKL下载页面下载适合的安装器并安装。在已经安装好VS的情况下,运行安装器后应该会出现Integrate with IDE字样,如下图在这里插入图片描述
  3. 在VS中新建C++控制台项目。进入项目后在项目属性页中启用oneMKL,如下图。在这里插入图片描述

二维Real to complex FFT的实现

基于递归的简单FFT2D实现如下

void fft(complex<float>* a, int n) {
    if (n <= 1) {
        return;
    }
    complex<float>* even = new complex<float>[n / 2];
    complex<float>* odd = new complex<float>[n / 2];
    for (int i = 0; 2 * i < n; i++) {
        even[i] = a[2 * i];
        odd[i] = a[2 * i + 1];
    }
    fft(even, n / 2);
    fft(odd, n / 2);
    double angle = -2 * pi / n;
    complex<float> w(1), wn(cos(angle), sin(angle));
    for (int i = 0; 2 * i < n; i++) {
        a[i] = even[i] + w * odd[i];
        a[i + n / 2] = even[i] - w * odd[i];
        w *= wn;
    }
    delete[] even;
    delete[] odd;
}

void fft2D(complex<float>* fft_result, float* r2c_data, int rows, int cols) {
    for (int i = 0; i < num_points; i++) {
        fft_result[i] = complex<float>(r2c_data[i], 0.0);
    }
    for (int i = 0; i < rows; i++) {
        fft(fft_result + i * cols, cols);
    }
    complex<float>* column = new complex<float>[rows];
    for (int i = 0; i < cols; i++) {
        for (int j = 0; j < rows; j++) {
            column[j] = fft_result[j * cols + i];
        }
        fft(column, rows);
        for (int j = 0; j < rows; j++) {
            fft_result[j * cols + i] = column[j];
        }
    }
    delete[] column;
}

基于oneAPI的随机数生成以及FFT2D计算

相较于官方提供的实例代码,为了方便后续速度比较,这里使用了

DftiSetValue(mkl_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE)

来避免生成的随机数数组被覆盖。由于上文所实现的fft2d函数数据存储格式为未经优化的复数-复数存储,为了方便后续正确性验证,这里使用了下述函数

DftiSetValue(mkl_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX)

来指定oneMKL FFT2D计算结果的存储格式。

void random_data_gen(float* r2c_data) {
    VSLStreamStatePtr stream;
    vslNewStream(&stream, VSL_BRNG_MT19937, 114514);
    vsRngUniform(VSL_RNG_METHOD_UNIFORM_STD, stream, num_points, r2c_data, 0.0, 1.0);
}

void one_fft(float* r2c_data, complex<float>* mkl_result, int dims[]) {
    DFTI_DESCRIPTOR_HANDLE mkl_handle = NULL;
    DftiCreateDescriptor(&mkl_handle, DFTI_SINGLE, DFTI_REAL, 2, dims);
    DftiSetValue(mkl_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE);
    DftiSetValue(mkl_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);
    DftiCommitDescriptor(mkl_handle);
    DftiComputeForward(mkl_handle, r2c_data, mkl_result);
}

结果比对

经过上述设置后,oneMKL FFT2D结果存储格式如下图有色色块部分(8 * 8)。故对于一个N * N的矩阵而言,比对范围为N * (N / 2 + 1)。
在这里插入图片描述
函数如下

void comparison(complex<float>* fft_result, complex<float>* mkl_result) {
    int col = NUM /2 + 1, row = NUM, index = 0;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            index = i * NUM + j;
            float dist = abs(fft_result[index] - mkl_result[index]);
            if (dist > 1e-4) {
                printf("结果不正确\n");
                return;
            }
        }
    }
    printf("结果正确\n");
}

结果展示

结果显示,oneMKL的时间优势是十分显著的。
在这里插入图片描述

结语

在比赛过程中,我了解了oneAPI的威力以及oneMKL工具的巨大潜力,也十分期待在未来的工作中继续进行oneAPI的应用。这次实现FFT2D算法的过程加深了我对傅里叶变换的记忆,而与oneMKL的对比结果也让我感受到了优化和加速工程问题的重要性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值