CUDA技術を利用したGPUコンピューティングの実際(後編) ―― FFTを利用した光波の伝播(フレネル回折)をGPUで高速計算

下馬場 朋禄,伊藤 智義

tag: 組み込み 半導体

技術解説 2008年10月 1日

 FFTを使用した畳み込みの計算には幾つか注意する点があります.一つ目は,FFTを使用した畳み込み計算が循環畳み込みとなることです.開口面と観察面のサイズがN×Nのときは,エリアジング(離散化したとき,サンプリング周波数によって入力信号の周波数成分が変換されてしまう現象)を避けるために,開口面と観察面を2N×2Nの領域に拡張して計算しなければなりません.拡張された領域は0で埋めておきます(ゼロ・パディング).またFFTの性質から,p(x,y)の象限交換(第1象限と第3象限の交換,および第2象限と第4象限の交換を行う.MatlabやOctave,Scilabなどのfftshiftに相当)を行う必要があります.

● CUFFTを使用したフレネル回折積分の実際

 まず,リスト6の(1)の部分で,計算に使用するホスト側の開口データと計算結果を格納するためのメモリ領域(h_buf),およびGPU側のグローバル・メモリ(d_buf1,d_buf2,d_buf3)を確保します.d_buf1~d_buf3はGPUの一時的な計算結果を格納するために使用します.確保するサイズはmem_sizeです.開口面と観察面は複素数で表現されるため,CUFFTに用意されている複素数を扱うためのcuComplex型で2N×2Nの領域を確保します.


~ 途中省略 ~

#include <cutil.h> //CUTIL用のヘッダ・ファイル
#include <cufft.h> //CUFFT用のヘッダ・ファイル

~ 途中省略 ~

  int N=512;
  int N2=N*2;

  double p=10.0e-6; //サンプリング間隔
  double lambda=633.0e-9; //赤色レーザ波長
  double d=0.5; //開口面と観察面の間の距離
  double PI=3.1415926535897932384626433832795; //円周率

  //メモリ確保
  int mem_size = sizeof(cufftComplex) * N2 * N2;
  cufftComplex* h_buf = (cufftComplex*) malloc( mem_size);

  cufftComplex* d_buf1, *d_buf2, *d_buf3;
  cudaMalloc( (void**) &d_buf1, mem_size);
  cudaMalloc( (void**) &d_buf2, mem_size);
  cudaMalloc( (void**) &d_buf3, mem_size);

  //開口データa(x,y)の作成と拡張 ←(1)
  for(int i=0;i<N2;i++){
    for(int j=0;j<N2;j++){
      int adr=j+i*N2;
      if(j>256-20 && j<256+20 && i>256-10 && i<256+10){
        h_buf[adr]=make_cuComplex(1.0, 0.0);
      }
      else{
        h_buf[adr]=make_cuComplex(0.0, 0.0);
    }
  }
  }

  //開口面a(x,y)のフーリエ変換 ←(2)
  cufftHandle fftplan;
  cufftPlan2d(&fftplan, N2, N2, CUFFT_C2C);
  cudaMemcpy( d_buf1, h_buf, mem_size,cudaMemcpyHostToDevice);
  cufftExecC2C(fftplan, d_buf1, d_buf2, CUFFT_FORWARD);

  //p(x,y)を算出 ←(3)
  for(int i=0;i<N2;i++){
    for(int j=0;j<N2;j++){
      int adr=j+i*N2;
      double dx=((double)( (j-N2/2) )) * p;
      double dy=((double)( (i-N2/2) )) * p;
      double tmp= 1.0 * PI* ( dx*dx + dy*dy ) / (lambda * d);
      h_buf[adr]=make_cuComplex(cos(tmp),sin(tmp));
    }
  }

  FFTShift(h_buf, N2, N2); //象限の交換

  //p(x,y)のFFT ←(4)
  cudaMemcpy( d_buf3, h_buf, mem_size,cudaMemcpyHostToDevice);
  cufftExecC2C(fftplan, d_buf3, d_buf1, CUFFT_FORWARD);

  //複素乗算 ←(5)
  dim3 block(16, 16, 1);
  dim3 grid(N2 / block.x, N2 / block.y, 1);
  KernelMult<<< grid, block>>>(d_buf2, d_buf1, d_buf3, N2, N2);

  //逆FFT ←(6)
  cufftExecC2C(fftplan, d_buf3, d_buf1, CUFFT_INVERSE);

  //計算結果を転送 ←(7)
  cudaMemcpy( h_buf, d_buf1, mem_size,cudaMemcpyDeviceToHost);

  //結果をビットマップ・ファイルとして保存 ←(8)
  Save("diffraction.bmp", N2, N2, h_buf);

  //開放 ←(9)
  cufftDestroy(fftplan);
  cudaFree(d_buf1);
  cudaFree(d_buf2);
  cudaFree(d_buf3);
  free(h_buf);

リスト6 CUFFTを使用したフレネル回折のプログラムの抜粋

 cuComplexは複素数の実数部と虚数部を格納できる構造体で,CUDAのバージョンによって定義が異なっています.CUDA 1.1では,cuComplexのメンバ変数としてx,yが用意されており,xが実数部に,yが虚数部に相当します.直接,このメンバ変数に対してアクセスを行ってもよいのですが,CUDAのバージョンが上がることでcuComplexの仕様が変更されるかもしれません(実際,CUDA 1.0とCUDA 1.1では定義が異なっている).そこで,cuComplexを操作するためのマクロがcuComplex.hに多数用意されているので,このマクロを介してアクセスすることにします.

組み込みキャッチアップ

お知らせ 一覧を見る

電子書籍の最新刊! FPGAマガジン No.12『ARMコアFPGA×Linux初体験』好評発売中

FPGAマガジン No.11『性能UP! アルゴリズム×手仕上げHDL』好評発売中! PDF版もあります

PICK UP用語

EV(電気自動車)

関連記事

EnOcean

関連記事

Android

関連記事

ニュース 一覧を見る
Tech Villageブログ

渡辺のぼるのロボコン・プロモータ日記

2年ぶりのブログ更新w

2016年10月 9日

Hamana Project

Hamana-8最終打ち上げ報告(その2)

2012年6月26日