CUDA袖口2D示例

时间:2016-04-27 12:06:34

标签: c++ cuda cufft

我目前正在开发一个必须实现2D-FFT的程序(用于交叉相关)。我用CUDA进行了1D FFT,这给了我正确的结果,我现在正在尝试实现2D版本。在线的例子和文档很少,我发现很难找出错误是什么。

到目前为止,我一直只使用cuFFT手册。

无论如何,我创建了两个5x5阵列并用1填充它们。我已将它们复制到GPU内存并完成正向FFT,然后将它们相乘,然后对结果进行ifft。这给了我一个值为650的5x5阵列。我希望在5x5阵列的一个插槽中得到一个值为25的DC信号。相反,我在整个阵列中得到650。

此外,我不允许在将信号复制到GPU内存后打印出信号的值。写作

cout << d_signal[1].x << endl;

给我一​​个访问权限违规。我在其他cuda程序中做了同样的事情,这不是一个问题。它是否与复杂变量的工作方式有关,还是人为错误?

如果有人指出哪里出了问题我会非常感激。这是代码

   #include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <helper_functions.h>
#include <helper_cuda.h>

#include <ctime>
#include <time.h>
#include <stdio.h>
#include <iostream>
#include <math.h>
#include <cufft.h>
#include <fstream>

using namespace std;
typedef float2 Complex;





__global__ void ComplexMUL(Complex *a, Complex *b)
{
    int i = threadIdx.x;
    a[i].x = a[i].x * b[i].x - a[i].y*b[i].y;
    a[i].y = a[i].x * b[i].y + a[i].y*b[i].x;
}


int main()
{


    int N = 5;
    int SIZE = N*N;


    Complex *fg = new Complex[SIZE];
    for (int i = 0; i < SIZE; i++){
        fg[i].x = 1; 
        fg[i].y = 0;
    }
    Complex *fig = new Complex[SIZE];
    for (int i = 0; i < SIZE; i++){
        fig[i].x = 1; // 
        fig[i].y = 0;
    }
    for (int i = 0; i < 24; i=i+5)
    {
        cout << fg[i].x << " " << fg[i + 1].x << " " << fg[i + 2].x << " " << fg[i + 3].x << " " << fg[i + 4].x << endl;
    }
    cout << "----------------" << endl;
    for (int i = 0; i < 24; i = i + 5)
    {
        cout << fig[i].x << " " << fig[i + 1].x << " " << fig[i + 2].x << " " << fig[i + 3].x << " " << fig[i + 4].x << endl;
    }
    cout << "----------------" << endl;

    int mem_size = sizeof(Complex)* SIZE;


    cufftComplex *d_signal;
    checkCudaErrors(cudaMalloc((void **) &d_signal, mem_size)); 
    checkCudaErrors(cudaMemcpy(d_signal, fg, mem_size, cudaMemcpyHostToDevice));

    cufftComplex *d_filter_kernel;
    checkCudaErrors(cudaMalloc((void **)&d_filter_kernel, mem_size));
    checkCudaErrors(cudaMemcpy(d_filter_kernel, fig, mem_size, cudaMemcpyHostToDevice));

    // cout << d_signal[1].x << endl;
    // CUFFT plan
    cufftHandle plan;
    cufftPlan2d(&plan, N, N, CUFFT_C2C);

    // Transform signal and filter
    printf("Transforming signal cufftExecR2C\n");
    cufftExecC2C(plan, (cufftComplex *)d_signal, (cufftComplex *)d_signal, CUFFT_FORWARD);
    cufftExecC2C(plan, (cufftComplex *)d_filter_kernel, (cufftComplex *)d_filter_kernel, CUFFT_FORWARD);

    printf("Launching Complex multiplication<<< >>>\n");
    ComplexMUL <<< 32, 256 >> >(d_signal, d_filter_kernel);

    // Transform signal back
    printf("Transforming signal back cufftExecC2C\n");
    cufftExecC2C(plan, (cufftComplex *)d_signal, (cufftComplex *)d_signal, CUFFT_INVERSE);

    Complex *result = new Complex[SIZE];
    cudaMemcpy(result, d_signal, sizeof(Complex)*SIZE, cudaMemcpyDeviceToHost);

    for (int i = 0; i < SIZE; i=i+5)
    {
        cout << result[i].x << " " << result[i + 1].x << " " << result[i + 2].x << " " << result[i + 3].x << " " << result[i + 4].x << endl;
    }

    delete result, fg, fig;
    cufftDestroy(plan);
    //cufftDestroy(plan2);
    cudaFree(d_signal);
    cudaFree(d_filter_kernel);

}

上面的代码给出了以下终端输出:

1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
----------------
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
----------------
Transforming signal cufftExecR2C
Launching Complex multiplication<<< >>>
Transforming signal back cufftExecC2C

625 625 625 625 625
625 625 625 625 625
625 625 625 625 625
625 625 625 625 625
625 625 625 625 625

2 个答案:

答案 0 :(得分:2)

这里有几个问题:

  1. 您正在为乘法内核中的输入数组的大小启动太多线程,因此应该在出现超出范围的内存错误时失败。我很惊讶您没有收到任何类型的运行时错误。
  2. 您认为fft / fft - dot产品 - ifft序列的预期解决方案是错误的。正确的解决方案是5x5矩阵,每个条目25个。
  3. 正如cuFFT文档中清楚描述的那样,库执行非标准化 FFT:
      

    cuFFT执行非标准化FFT;也就是说,对输入数据集执行前向FFT,然后对得到的集合进行逆FFT,产生等于输入的数据,按元素数量进行缩放。通过数据集大小的倒数来缩放变换,留给用户按照适合的方式执行。

  4. 因此,通过我的计算,代码的正确输出解决方案应该是5x5矩阵,每个条目中有625个,这将被标准化为5x5矩阵,每个条目25个,即。预期的结果。我不明白(1)的问题是如何产生不同的结果,因为乘法内核应该失败。

    TLDR;没什么可看的,继续前进......

答案 1 :(得分:0)

这给了我一个5x5数组,其值为650 :它读取625,即5 * 5 * 5 * 5。您使用的卷积算法需要N * N的补充除法。实际上,在cufft中,正向变换中没有归一化系数。因此,您的卷积不能是频域中两个场的简单乘法。 (有些人称之为数学家DFT,而不是医生DFT)。

此外,我不允许在将信号复制到GPU内存后打印出信号值 :这是标准的CUDA行为。在设备上分配内存时,数据存在于设备内存地址空间中,如果没有额外的努力,CPU无法访问。搜索托管内存或 zerocopy 以便从PCI Express的两端访问数据(这在许多其他帖子中都有讨论)。