You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于cuFFT的傅里叶变换:复-复变换是否更高效?

实值PDE傅里叶空间时间积分:CUFFT复-复变换为何比实-复/复-实变换更快?

我正在用CUDA/C++编写傅里叶空间中偏微分方程(PDE)的时间积分代码,需要演化一个实值数组。我实现了两种逻辑完全一致的方案:

方案一:全复数数组+复-复变换

所有数组均定义为复数类型,执行复-复傅里叶变换。为保证实值数组始终保持实值,每个时间步会将其虚部置0,消除累积的微小数值误差。

float *real;
cufftComplex *fourier;
...
for (int t = 0; t < totalTime; t++)
{
   cufftExecC2R(plan1, fourier, real);
   // 实空间计算操作
   cufftExecR2C(plan2, real, fourier);
   integrate<<<blocks, threads>>>(fourier, other parameters);
}

方案二:实数组+实-复/复-实变换

用浮点数组存储实值数据,复数数组存储傅里叶变换结果,执行复-实和实-复变换。理论上该方案内存效率更高:实数组替代复数组,傅里叶空间的复数组仅需存储N/2+1个元素。

cufftComplex *real;
cufftComplex *fourier;
...
for (int t = 0; t < totalTime; t++)
{
   cufftExecC2C(plan, fourier, real, CUFFT_INVERSE);
   // 实空间计算操作
   cufftExecC2C(plan, real, fourier, CUFFT_FORWARD); // 仅需一个变换计划
   integrate<<<blocks, threads>>>(fourier, other parameters);
}

但实际测试中,方案一在我的硬件上始终比方案二快约20%。这个结果符合预期吗?复-复变换在计算效率上真的优于实-复/复-实变换?


最小复现代码

以下两段代码可直接编译。在我的硬件上,方案二平均耗时1.1-1.2秒,方案一平均耗时约0.9秒。由于实际程序运行时间极长,这一微小差异会被显著放大。

helper_cuda.h和helper_functions.h并非编译必需文件,可在CUDA Toolkit示例代码的Common目录中找到。

仅使用复-复变换的代码

#include <iostream>
#include <fstream>
#include <math.h>
#include <random>
#include <fftw3.h>
#include <complex.h>


#include <cuda_runtime.h>
#include <cufft.h>
#include <cufftXt.h>
#include <helper_cuda.h>
#include <helper_functions.h>

using namespace std;

void printProgressbar(int t, int Nsteps, int length)
{
    int progress = t*100/Nsteps;
    int sprogress = t*length/Nsteps;
    int left = length - sprogress - 1;
    cout << "Progress [";
    for (int i = 0; i < sprogress; i++)
        cout << "=";
    cout << ">";
    for (int i = 0; i < left; i++)
        cout << " ";
    cout << "] " << progress << "%   \r";
    cout.flush();
}

int main()
{
    int N = 128;
    int Nsteps = 50000;
    int numberofsnaps = 5;
    int plot_steps = Nsteps/numberofsnaps;

    cufftComplex *phi_host = new cufftComplex[N*N];
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<> dis(-0.01,0.01);
    for (int i = 0; i < N*N; i++)
    {
        phi_host[i].x = (float)dis(gen);
        phi_host[i].y = 0.0f;
    }

    // Device arrays
    cufftComplex *phi_real;
    cufftComplex *phi_comp;

    cudaMalloc(reinterpret_cast<void **>(&phi_real), N * N * sizeof(cufftComplex));
    cudaMalloc(reinterpret_cast<void **>(&phi_comp), N * N * sizeof(cufftComplex));

    cufftHandle plan_c2c;
    cufftPlan2d(&plan_c2c, N, N, CUFFT_C2C);

    cudaMemcpy(phi_real, phi_host, N * N * sizeof(cufftComplex), cudaMemcpyHostToDevice);
    cufftExecC2C(plan_c2c, phi_real, phi_comp, CUFFT_FORWARD);

    for (int t = 0; t < Nsteps; t++)
    {
        // Output data
        if (t % plot_steps == 0)
        {
            printProgressbar(t,Nsteps,25);
            cudaMemcpy(phi_host, phi_real, N * N * sizeof(cufftComplex), cudaMemcpyDeviceToHost);
        }
        cufftExecC2C(plan_c2c, phi_comp, phi_real, CUFFT_INVERSE);
        cufftExecC2C(plan_c2c, phi_real, phi_comp, CUFFT_FORWARD);
    }
    cout << "Progress [========================>] 100%" << endl;
    return 0;
}

使用实-复和复-实变换的代码

#include <iostream>
#include <fstream>
#include <math.h>
#include <random>
#include <fftw3.h>
#include <complex.h>


#include <cuda_runtime.h>
#include <cufft.h>
#include <cufftXt.h>
#include <helper_cuda.h>
#include <helper_functions.h>
using namespace std;

void printProgressbar(int t, int Nsteps, int length)
{
    int progress = t*100/Nsteps;
    int sprogress = t*length/Nsteps;
    int left = length - sprogress - 1;
    cout << "Progress [";
    for (int i = 0; i < sprogress; i++)
        cout << "=";
    cout << ">";
    for (int i = 0; i < left; i++)
        cout << " ";
    cout << "] " << progress << "%   \r";
    cout.flush();
}

int main()
{
    int N = 128;
    int Np = N/2 + 1;
    int Nsteps = 50000;
    int numberofsnaps = 5;
    int plot_steps = Nsteps/numberofsnaps;

    float *phi_host = new float[N*N];
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<> dis(-0.01,0.01);
    for (int i = 0; i < N*N; i++)
    {
        phi_host[i] = (float)dis(gen);
    }

    // Device arrays
    float *phi_real;
    cufftComplex *phi_comp;

    cudaMalloc(reinterpret_cast<void **>(&phi_real), N * N * sizeof(float));
    cudaMalloc(reinterpret_cast<void **>(&phi_comp), N * Np * sizeof(cufftComplex));

    cufftHandle plan_r2c, plan_c2r;
    cufftPlan2d(&plan_r2c, N, N, CUFFT_R2C);
    cufftPlan2d(&plan_c2r, N, N, CUFFT_C2R);

    cudaMemcpy(phi_real, phi_host, N * N * sizeof(float), cudaMemcpyHostToDevice);
    cufftExecR2C(plan_r2c, phi_real, phi_comp);

    for (int t = 0; t < Nsteps; t++)
    {
        // Output data
        if (t % plot_steps == 0)
        {
            printProgressbar(t,Nsteps,25);
            cudaMemcpy(phi_host, phi_real, N * N * sizeof(float), cudaMemcpyDeviceToHost);
        }
        cufftExecC2R(plan_c2r, phi_comp, phi_real);
        cufftExecR2C(plan_r2c, phi_real, phi_comp);
    }
    cout << "Progress [========================>] 100%" << endl;
    return 0;
}

内容的提问来源于stack exchange,提问作者MyUserIsThis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 14:22:02