【问题标题】:CUFFT | cannot figure out a simple example袖口 |想不出一个简单的例子
【发布时间】:2013-10-12 13:10:54
【问题描述】:

我整天都在苦苦挣扎,试图让一个基本的 CUFFT 示例正常工作。但是我遇到了一个我无法识别的小问题。基本上我有一个带有 x 和 y 坐标的线性二维数组 vx 。然后我只计算一个向前然后向后的 CUFFT(就地),就这么简单。然后我复制回数组 vx,通过 NX*NY 对其进行归一化,然后显示。

#define NX 32
#define NY 32
#define LX (2*M_PI)
#define LY (2*M_PI)
float *x = new float[NX*NY];
float *y = new float[NX*NY];
float *vx = new float[NX*NY];
for(int j = 0; j < NY; j++){
    for(int i = 0; i < NX; i++){
        x[j*NX + i] = i * LX/NX;
        y[j*NX + i] = j * LY/NY;
        vx[j*NX + i] = cos(x[j*NX + i]);
    }
}
float *d_vx;
CUDA_CHECK(cudaMalloc(&d_vx, NX*NY*sizeof(float)));
CUDA_CHECK(cudaMemcpy(d_vx, vx, NX*NY*sizeof(float), cudaMemcpyHostToDevice));
cufftHandle planr2c;
cufftHandle planc2r;
CUFFT_CHECK(cufftPlan2d(&planr2c, NY, NX, CUFFT_R2C));
CUFFT_CHECK(cufftPlan2d(&planc2r, NY, NX, CUFFT_C2R));
CUFFT_CHECK(cufftSetCompatibilityMode(planr2c, CUFFT_COMPATIBILITY_NATIVE));
CUFFT_CHECK(cufftSetCompatibilityMode(planc2r, CUFFT_COMPATIBILITY_NATIVE));
CUFFT_CHECK(cufftExecR2C(planr2c, (cufftReal *)d_vx, (cufftComplex *)d_vx));
CUFFT_CHECK(cufftExecC2R(planc2r, (cufftComplex *)d_vx, (cufftReal *)d_vx));
CUDA_CHECK(cudaMemcpy(vx, d_vx, NX*NY*sizeof(cufftReal), cudaMemcpyDeviceToHost));
for (int j = 0; j < NY; j++){
    for (int i = 0; i < NX; i++){
        printf("%.3f ", vx[j*NX + i]/(NX*NY));
    }
    printf("\n");
}

当 vx 定义为 cos(x) 或 sin(x) 时,它可以正常工作,但是当使用 sin(y) 或 cos(y) 时,它会返回正确的函数(sin 或 cos),但只有一半幅度(即在 0.5 和 -0.5 之间而不是 1 和 -1 之间振荡)!请注意,使用 sin(2*y) 或 cos(2*y)(或 sin(4*y)、cos(4*y)、...)可以正常工作。有什么想法吗?

【问题讨论】:

  • 为什么你的第一个 cudaMemcpy 操作指定 cudaMemcpyDeviceToHost?这没有任何意义,并且与指针的顺序不匹配。如果您的 CUDA_CHECK 宏没有在那里抛出错误,那么您的宏有问题。
  • 对不起,我没有复制/粘贴正确的行,在我的代码中,复制的意义是正确的。我更新
  • 你之前的那行代码不可能在任何地方都是正确的。如果该行代码出现在您的代码中的其他地方,那是错误的。顺便说一句,SO 希望您为此类问题提供 SSCCE.org 代码。
  • 我刚刚以双精度重新计算它并且它可以工作。但我无法解释原因,可能是我的内存布局有问题。

标签: cuda fft cufft


【解决方案1】:

这里的问题是,就地实数到复数转换的输入和输出是一种复杂类型,其大小与输入实数数据不同(它是输入实数的两倍)。您没有分配足够的内存来保存实数到复数转换的中间复数结果。引用文档:

cufftExecR2C() (cufftExecD2Z()) 执行单精度 (双精度)实数到复数,隐式前向,CUFFT 改造计划。 CUFFT 使用指向的 GPU 内存作为输入数据 idata 参数。该函数存储非冗余傅里叶 odata 数组中的系数。指向 idata 和 odata 的指针都是 需要以单精度与 cufftComplex 数据类型对齐 以双精度转换和 cufftDoubleComplex 数据类型 变换。

解决方案是分配第二个设备缓冲区来保存中间结果,或者扩大就地分配,使其足够大以容纳复杂数据。所以核心转换代码变成了这样的:

float *d_vx;
CUDA_CHECK(cudaMalloc(&d_vx, NX*NY*sizeof(cufftComplex)));
CUDA_CHECK(cudaMemcpy(d_vx, vx, NX*NY*sizeof(cufftComplex), cudaMemcpyHostToDevice));
cufftHandle planr2c;
cufftHandle planc2r;
CUFFT_CHECK(cufftPlan2d(&planr2c, NY, NX, CUFFT_R2C));
CUFFT_CHECK(cufftPlan2d(&planc2r, NY, NX, CUFFT_C2R));
CUFFT_CHECK(cufftSetCompatibilityMode(planr2c, CUFFT_COMPATIBILITY_NATIVE));
CUFFT_CHECK(cufftSetCompatibilityMode(planc2r, CUFFT_COMPATIBILITY_NATIVE));
CUFFT_CHECK(cufftExecR2C(planr2c, (cufftReal *)d_vx, d_vx));
CUFFT_CHECK(cufftExecC2R(planc2r, d_vx, (cufftReal *)d_vx));
CUDA_CHECK(cudaMemcpy(vx, d_vx, NX*NY*sizeof(cufftComplex), cudaMemcpyDeviceToHost));

[免责声明:在浏览器中编写,从未编译或测试,使用风险自负]

请注意,您需要调整主机代码以匹配输入和数据的大小和类型。

作为最后的评论,添加额外的 8 或 10 行代码来将您发布的内容变成一个可编译、可运行的示例,以供尝试帮助您的人使用是不是很难?

【讨论】:

  • 就是这样!并且由于酉模在阵列的末端,它丢失了,因此损失了一半的能量。下次我会改进演示文稿
  • 供将来参考:这不是真的。对于 R2C/C2R 转换,您只需要复杂类型的大约一半内存。你有 (NX/2+1)*NY 复数元素,即 (NX+2)*NY 偶数 NX 实元素和 (NX+1)*NY 奇数 NX 实元素
  • 请注意,在当前版本的 CUFFT(9.0 之后)中,cufftSetCompatibilityMode 函数已被删除,因此 CUFFT 期望在 2D 就地 R2C 转换中使用 padding
猜你喜欢
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-30
  • 1970-01-01
相关资源
最近更新 更多