1. 介绍

本文档介绍了沐曦 MXMACA® 快速傅立叶变换 (FFT) 产品 mcFFT。mcFFT库旨在为沐曦 GPU提供高性能支持。

FFT是一种分治算法,用于高效地计算复数或实数集的离散傅里叶变换。 它是计算物理学和通用信号处理中最重要、应用最广泛的数值算法之一。 mcFFT 库为在沐曦 GPU 上计算FFT提供了一个简单的接口,允许用户可以在高度优化和测试的FFT库中快速利用GPU的浮点计算能力和并行性。

mcFFT产品在沐曦 GPU上广泛且有效支持各种FFT输入和选项。该版本的mcFFT库支持以下功能:

  • 高度优化的算法可以适用于形如 \(2^{a} \times 3^{b} \times 5^{c} \times 7^{d}\) 的输入数据上。通常来说,素数因子越小,性能越好。即,二次幂最快

  • \(O\left( n\log n \right)\) 算法适用于各种大小的输入数据

  • 支持单精度(32bit 浮点数)和双精度(64bit浮点数)的FFT。低精度浮点数的FFT拥有更好的性能

  • 支持复数和实数的输入和输出。实数输入输出和复数输入输出相比较而言,实数要求的算力更少并且处理速度更快。支持的类型包括:

    • C2C - 复数输入到复数输出的FFT

    • R2C - 实数输入到复数输出的FFT

    • C2R - 对称复数输入到实数输出的FFT

  • 支持一维,二维和三维变换

  • 支持复数个一维,二维和三维同时变换。和单个变换相比,批处理变换更加高效

  • 支持原地变换和异地变换

  • 支持任一维度内和维度间的元素步长(支持跨步布局)

  • 支持流式执行。允许异步计算和数据传输

1.1. 安装mcFFT

\[MXMACA\begin{cases} library & \begin{cases} mcFFT \\ \vdots \end{cases} \\ \vdots \\ \vdots \end{cases} \]

mcFFT是随MXMACA工具包一起发布的库。MXMACA工具包的安装请参见《曦云系列通用GPU 快速上手指南》。安装完成后,请确保设置了环境变量MACA_PATH。

export MACA_PATH=/your/maca/path

mcFFT API 相关文档如下:

#header location:
${MACA_PATH}/include/mcfft

#lib location:
${MACA_PATH}/lib/libmcfft.so
${MACA_PATH}/lib/libmcfft-device-0.so
${MACA_PATH}/lib/libmcfft-device-1.so
${MACA_PATH}/lib/libmcfft-device-2.so
${MACA_PATH}/lib/libmcfft-device-3.so

1.2. Hello mcFFT

以下示例代码演示了使用mcFFT库API编写的C语言应用程序:

#CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(mcfft_samples)
if(NOT DEFINED ENV{MACA_PATH})
    message(FATAL_ERROR "not defined environment variable: MACA_PATH")
endif()
INCLUDE_DIRECTORIES($ENV{MACA_PATH}/include/)
INCLUDE_DIRECTORIES($ENV{MACA_PATH}/include/mcfft/)
INCLUDE_DIRECTORIES($ENV{MACA_PATH}/mcr/)
LINK_DIRECTORIES($ENV{MACA_PATH}/lib)
set(LINK_LIBS
    mc_runtime
    mcfft
    mcfft-device-0
    mcfft-device-1
    mcfft-device-2
    mcfft-device-3
    )
set(sample_list complex_1d)

foreach(sample ${sample_list})
  add_executable(mcfft_${sample} ${sample}.cpp)
  target_link_libraries(mcfft_${sample} ${LINK_LIBS})
endforeach()
//complex_1d.cpp, 1D, C2C, in-place
//-----------------------------------------------------------
#include <cassert>
#include <complex>
#include <iostream>
#include <vector>
#include <random>
#include <mc_runtime.h>
#include "mcfft.h"
#define NX 8
#define BATCH 1

int main(int argc, char* argv[])
{
   std::cout << "mcFFT complex 1d FFT example\n";
   mcfftHandle plan;
   mcError_t err;

   //在主机处初始化数据:
   std::vector<std::complex<float>> data(NX*BATCH);
   std::vector<std::complex<float>> outData(NX*BATCH);
   std::cout << "Input:\n";

   for(int b = 0; b < BATCH; b++)
   {
      for(int n = 0; b < NX; n++)
      {
         std::mt19937 gen(n);
         const float x = (float)gen() / (float)gen.max();
         const float y = (float)gen() / (float)gen.max();
         const std::complex<float> val(x,y);
         data[NX*b + n] = val;
         std::cout<<data[NX*b + n]<<" ";
      }
   }

   // 创建设备对象和计划:
   mcfftComplex *devPtrData=nullptr;
   err=mcMalloc((void**)&devPtrData, sizeof(mcfftComplex)*NX*BATCH);
   if (err != mcSuccess)
   {
     fprintf(stderr, "Error: Failed to allocate\n");
     return EXIT_FAILURE;
   }

   if (mcfftPlan1d(&plan, NX, MCFFT_C2C, BATCH) != mcSuccess)
   {
     fprintf(stderr, "mcFFT Error: Plan creation failed\n");
     return EXIT_FAILURE;
   }

   //复制数据
   err = mcMemcpy(devPtrData, data.data(), sizeof(mcfftComplex)*NX*BATCH, mcMemcpyHostToDevice);
   if (err != mcSuccess)
   {
     fprintf(stderr, "Error: Failed to copy host to device\n");
     return EXIT_FAILURE;
   }

   // 执行正向变换和原地变换
   if (mcfftExecC2C(plan, devPtrData, devPtrData, MCFFT_FORWARD) != mcSuccess)
   {
     fprintf(stderr, "mcFFT error: ExecC2C Forward failed\n");
     return EXIT_FAILURE;
   }

   // 执行逆向变换和原地变换
   if (mcfftExecC2C(plan, devPtrData, devPtrData, MCFFT_INVERSE) != mcSuccess)
   {
     fprintf(stderr, "mcFFT error: ExecC2C Inverse failed\n");
     return EXIT_FAILURE;
   }

   /*
   *   在所有任务完成之前,可能无法立即获得结果
   *
   */

   if (mcDeviceSynchronize() != mcSuccess)
   {
     fprintf(stderr, "Error: Failed to synchronize\n");
     return EXIT_FAILURE;
   }

   err = mcMemcpy(outData.data(), devPtrData,  sizeof(mcfftComplex)*NX*BATCH, mcMemcpyDeviceToHost);
   if (err != mcSuccess)
   {
     fprintf(stderr, "Error: Failed to copy device to host\n");
     return EXIT_FAILURE;
   }

   const float overN = 1.0f / Nx;
   float       error = 0.0f;
   for(size_t i = 0; i < data.size(); i++)
   {
     float diff = std::max(std::abs(data[i].real() - outData[i].real() * overN),
                           std::abs(data[i].imag() - outData[i].imag() * overN));
     if(diff > error)
     {
         error = diff;
     }
   }

   std::cout << "Transformed back:\n";
   for(size_t i = 0; i < outData.size(); i++)
   {
     std::cout << outData[i]*overN << " ";
   }
   std::cout << std::endl;
   std::cout << "Maximum error: " << error << "\n";

   mcfftDestroy(plan);
   mcFree(devPtrData);
}

上述文件(CMakeList.txt和complex_1d.cpp)在同一个目录中,比如 /your/example/path 。示例编译步骤如下:

$ cd /your/example/path
$ mkdir build && cd build
$ cmake ..
$ make