定期与cufft-fftw复杂到实际变换的差异问题

时间:2013-01-01 19:35:21

标签: cuda fft c99 fftw

对于我的论文,我必须使用CUDA优化一个特殊的MPI-Navier Stokes-Solver程序。原始程序使用FFTW来解决几个PDE。详细地,几个上三角矩阵在二维中被傅立叶变换,但是作为一维阵列处理。目前,我正在努力处理部分原始代码:( N总是设置为64)

原件:

//Does the complex to real in place fft an normalizes
void fftC2R(double complex *arr) {
  fftw_execute_dft_c2r(plan_c2r, (fftw_complex*)arr, (double*)arr);
  //Currently ignored: Normalization
  /*  for(int i=0; i<N*(N/2+1); i++)
arr[i] /= (double complex)sqrt((double complex)(N*N));*/
}

void doTimeStepETDRK2_nonlin_original() {
      //calc velocity
      ux[0] = 0;
      uy[0] = 0;

      for(int i=1; i<N*(N/2+1); i++) {
         ux[i] =  I*kvec[1][i]*qvec[i] / kvec[2][i];
         uy[i] = -I*kvec[0][i]*qvec[i] / kvec[2][i];
      }

      fftC2R(ux);
      fftC2R(uy);

      //do some stuff here...
      //...
      return;
}

将ux和uy分配为(双复数组):

ux = (double complex*)fftw_malloc(N*(N/2+1) * sizeof(double complex));
uy = (double complex*)fftw_malloc(N*(N/2+1) * sizeof(double complex));

fft-plan创建为:

plan_c2r = fftw_plan_dft_c2r_2d(N, N,(fftw_complex*) qvec, (double*)qvec, FFTW_ESTIMATE);

其中qvec的分配方式与ux和uy相同,并且数据类型为double complex。


以下是CUDA代码的相关部分:

NN2_VecSetZero_and_init<<<block_size,grid_size>>>();
cudaSafeCall(cudaDeviceSynchronize());
cudaSafeCall(cudaGetLastError());

int err = (int)cufftExecZ2D(cu_plan_c2r,(cufftDoubleComplex*)sym_ux,(cufftDoubleReal*)sym_ux);

if (err != CUFFT_SUCCESS ) {
    exit(EXIT_FAILURE);
    return;
}

err = (int)cufftExecZ2D(cu_plan_c2r,(cufftDoubleComplex*)sym_uy,(cufftDoubleReal*)sym_uy);

if (err != CUFFT_SUCCESS ) {
    exit(EXIT_FAILURE);
    return;
}

//do some stuff here...
//...
return;

将sim_ux和sim_uy分配为:

cudaMalloc((void**)&sym_ux, N*(N/2+1)*sizeof(cufftDoubleComplex));
cudaMalloc((void**)&sym_uy, N*(N/2+1)*sizeof(cufftDoubleComplex));

相关袖口部件的初始化看起来像

if (cufftPlan2d(&cu_plan_c2r,N,N, CUFFT_Z2D) != CUFFT_SUCCESS){
    exit(EXIT_FAILURE);
    return -1;
}

if (cufftPlan2d(&cu_plan_r2c,N,N, CUFFT_D2Z)  != CUFFT_SUCCESS){
    exit(EXIT_FAILURE);
    return -1;
}

if ( cufftSetCompatibilityMode ( cu_plan_c2r , CUFFT_COMPATIBILITY_FFTW_ALL) != CUFFT_SUCCESS ) {
    exit(EXIT_FAILURE);
    return -1;
}

if ( cufftSetCompatibilityMode ( cu_plan_r2c , CUFFT_COMPATIBILITY_FFTW_ALL) != CUFFT_SUCCESS ) {
    exit(EXIT_FAILURE);
    return -1;
}


所以我使用完全的FFTW兼容性,我用FFTW调用模式调用每个函数。 当我运行两个版本时,我收到的ux和uy(sim_ux和sim_uy)的结果差不多。但是在阵列的周期性位置,Cufft似乎忽略了这些元素,其中FFTW将这些元素的实部设置为零并计算复杂的部分(数组太大而无法在此处显示)。发生这种情况的步骤数是N / 2 + 1。所以我相信,我并没有完全理解Cufft和FFTW之间的fft-padding理论。 在调用Cufft-executions之前,我可以排除这些数组之间的任何先前的差异。所以上面代码的任何其他数组都不相关 我的问题是:我是否过于乐观地使用几乎100%的FFTW呼叫方式?我必须在ffts之前处理我的阵列吗? Cufft文档说,我必须调整数据输入和输出数组的大小。但是,当我在进行原地转换时,我怎么能这样做呢?我真的不想离原始代码太远了,我不想再为每个fft调用使用复制指令,因为内存有限,数组应该尽可能长时间地保留并在gpu上处理。 / p>

我很感谢每一个提示,批评声明或想法!

我的配置:

  • 编译器:gcc 4.6(C99标准)
  • MPI-Package:mvapich2-1.5.1p1(由于减少了单处理调试模式,不应该发挥作用)
  • CUDA-Version:4.2
  • GPU:CUDA-arch-compute_20(NVIDIA GeForce GTX 570)
  • FFTW 3.3

1 个答案:

答案 0 :(得分:1)

一旦我与CUFFT有关,我开始工作的唯一解决方案是独家使用“cu_plan_c2c” - 真实和复杂数组之间有简单的转换:

- 使用0填充复杂零件以模拟cu_plan_r2c

-use atan2(not atan)对复杂结果进行模拟cu_plan_c2r

很抱歉没有指出任何更好的解决方案,但这就是我最终解决这个问题的方法。希望你没有进入任何低内存cpu侧的硬盘....