代码之家  ›  专栏  ›  技术社区  ›  user3214281

CUDA内存拷贝和cuFFT的异步执行

  •  3
  • user3214281  · 技术社区  · 11 年前

    我有一个CUDA程序,可以计算FFT的大小 50000 。目前,我将整个阵列复制到GPU并执行cuFFT。现在,我正在尝试优化程序,NVIDIA Visual Profiler告诉我通过并行计算的并发性隐藏内存副本。我的问题是:

    例如,是否可以复制第一个 5000 元素,然后开始计算,然后将下一组数据并行复制到计算等?

    由于DFT基本上是时间值的和乘以复指数函数,我认为应该可以“分块”计算FFT。

    cufft支持这一点吗?一般来说,这是一个好的计算方法吗?

    编辑

    更清楚地说,我不想在不同的阵列上并行计算不同的FFT。假设我在时域中有一个很大的正弦信号轨迹,我想知道信号中的频率。我的想法是,例如,将信号长度的三分之一复制到GPU,然后复制下三分之一,并使用已复制的输入值的前三分之一并行计算FFT。然后复制最后三分之一并更新输出值,直到处理完所有时间值。因此,最终应该有一个输出阵列,其峰值位于窦的频率。

    1 回复  |  直到 8 年前
        1
  •  5
  •   Vitality    11 年前

    请考虑上述意见,特别是:

    1. 如果计算FFT Npartial 元素,您将得到 N部分 元素;
    2. (跟随Robert Crovella)cuFFT调用启动之前,cuFFT所需的所有数据必须驻留在设备上,这样您就无法将数据分割为单个cuFFT操作的片段,并在所有片段都在GPU上之前开始该操作;此外,cuFFT调用是不透明的;

    考虑到以上两点,我认为只有正确使用,你才能“效仿”你想要实现的目标 零填充 以下面代码所示的方式。正如你将看到的,让 N 通过将数据划分为 NUM_STREAMS ,代码执行 NUM_STREAMS(字符串) 零填充的 流式传输的 cuFFT调用的大小 N 。在cuFFT之后,您必须合并(求和)部分结果。

    #include <stdio.h>
    
    #include <cufft.h>
    
    #define BLOCKSIZE 32
    #define NUM_STREAMS 3
    
    /**********/
    /* iDivUp */
    /*********/
    int iDivUp(int a, int b) { return ((a % b) != 0) ? (a / b + 1) : (a / b); }
    
    /********************/
    /* CUDA ERROR CHECK */
    /********************/
    #define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
    inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
    {
        if (code != cudaSuccess) 
        {
            fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
            if (abort) exit(code);
        }
    }
    
    /******************/
    /* SUMMING KERNEL */
    /******************/
    __global__ void kernel(float2 *vec1, float2 *vec2, float2 *vec3, float2 *out, int N) {
    
        int tid = threadIdx.x + blockIdx.x * blockDim.x;
    
        if (tid < N) {
            out[tid].x = vec1[tid].x + vec2[tid].x + vec3[tid].x;
            out[tid].y = vec1[tid].y + vec2[tid].y + vec3[tid].y;
        }
    
    }
    
    
    /********/
    /* MAIN */
    /********/
    int main()
    {
        const int N = 600000;
        const int Npartial = N / NUM_STREAMS;
    
        // --- Host input data initialization
        float2 *h_in1 = new float2[Npartial];
        float2 *h_in2 = new float2[Npartial];
        float2 *h_in3 = new float2[Npartial];
        for (int i = 0; i < Npartial; i++) {
            h_in1[i].x = 1.f;
            h_in1[i].y = 0.f;
            h_in2[i].x = 1.f;
            h_in2[i].y = 0.f;
            h_in3[i].x = 1.f;
            h_in3[i].y = 0.f;
        }
    
        // --- Host output data initialization
        float2 *h_out = new float2[N];
    
        // --- Registers host memory as page-locked (required for asynch cudaMemcpyAsync)
        gpuErrchk(cudaHostRegister(h_in1, Npartial*sizeof(float2), cudaHostRegisterPortable));
        gpuErrchk(cudaHostRegister(h_in2, Npartial*sizeof(float2), cudaHostRegisterPortable));
        gpuErrchk(cudaHostRegister(h_in3, Npartial*sizeof(float2), cudaHostRegisterPortable));
    
        // --- Device input data allocation
        float2 *d_in1;          gpuErrchk(cudaMalloc((void**)&d_in1, N*sizeof(float2)));
        float2 *d_in2;          gpuErrchk(cudaMalloc((void**)&d_in2, N*sizeof(float2)));
        float2 *d_in3;          gpuErrchk(cudaMalloc((void**)&d_in3, N*sizeof(float2)));
        float2 *d_out1;         gpuErrchk(cudaMalloc((void**)&d_out1, N*sizeof(float2)));
        float2 *d_out2;         gpuErrchk(cudaMalloc((void**)&d_out2, N*sizeof(float2)));
        float2 *d_out3;         gpuErrchk(cudaMalloc((void**)&d_out3, N*sizeof(float2)));
        float2 *d_out;          gpuErrchk(cudaMalloc((void**)&d_out, N*sizeof(float2)));
    
        // --- Zero padding
        gpuErrchk(cudaMemset(d_in1, 0, N*sizeof(float2)));
        gpuErrchk(cudaMemset(d_in2, 0, N*sizeof(float2)));
        gpuErrchk(cudaMemset(d_in3, 0, N*sizeof(float2)));
    
        // --- Creates CUDA streams
        cudaStream_t streams[NUM_STREAMS];
        for (int i = 0; i < NUM_STREAMS; i++) gpuErrchk(cudaStreamCreate(&streams[i]));
    
        // --- Creates cuFFT plans and sets them in streams
        cufftHandle* plans = (cufftHandle*) malloc(sizeof(cufftHandle)*NUM_STREAMS);
        for (int i = 0; i < NUM_STREAMS; i++) {
            cufftPlan1d(&plans[i], N, CUFFT_C2C, 1);
            cufftSetStream(plans[i], streams[i]);
        }
    
        // --- Async memcopyes and computations
        gpuErrchk(cudaMemcpyAsync(d_in1, h_in1, Npartial*sizeof(float2), cudaMemcpyHostToDevice, streams[0]));
        gpuErrchk(cudaMemcpyAsync(&d_in2[Npartial], h_in2, Npartial*sizeof(float2), cudaMemcpyHostToDevice, streams[1]));
        gpuErrchk(cudaMemcpyAsync(&d_in3[2*Npartial], h_in3, Npartial*sizeof(float2), cudaMemcpyHostToDevice, streams[2]));
        cufftExecC2C(plans[0], (cufftComplex*)d_in1, (cufftComplex*)d_out1, CUFFT_FORWARD);
        cufftExecC2C(plans[1], (cufftComplex*)d_in2, (cufftComplex*)d_out2, CUFFT_FORWARD);
        cufftExecC2C(plans[2], (cufftComplex*)d_in3, (cufftComplex*)d_out3, CUFFT_FORWARD);
    
        for(int i = 0; i < NUM_STREAMS; i++) gpuErrchk(cudaStreamSynchronize(streams[i]));
    
        kernel<<<iDivUp(BLOCKSIZE,N), BLOCKSIZE>>>(d_out1, d_out2, d_out3, d_out, N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
    
        gpuErrchk(cudaMemcpy(h_out, d_out, N*sizeof(float2), cudaMemcpyDeviceToHost));
    
        for (int i=0; i<N; i++) printf("i = %i; real(h_out) = %f; imag(h_out) = %f\n", i, h_out[i].x, h_out[i].y);
    
        // --- Releases resources
        gpuErrchk(cudaHostUnregister(h_in1));
        gpuErrchk(cudaHostUnregister(h_in2));
        gpuErrchk(cudaHostUnregister(h_in3));
        gpuErrchk(cudaFree(d_in1));
        gpuErrchk(cudaFree(d_in2));
        gpuErrchk(cudaFree(d_in3));
        gpuErrchk(cudaFree(d_out1));
        gpuErrchk(cudaFree(d_out2));
        gpuErrchk(cudaFree(d_out3));
        gpuErrchk(cudaFree(d_out));
    
        for(int i = 0; i < NUM_STREAMS; i++) gpuErrchk(cudaStreamDestroy(streams[i]));
    
        delete[] h_in1;
        delete[] h_in2;
        delete[] h_in3;
        delete[] h_out;
    
        cudaDeviceReset();  
    
        return 0;
    }
    

    这是上述代码在开普勒K20c卡上运行时的时间线。如您所见,计算与异步内存传输重叠。

    enter image description here