1

基本的な CUFFT の例を適切に動作させようとして、私は一日中苦労していました。しかし、特定できない小さな問題に遭遇しました。基本的に、x 座標と y 座標を持つ線形 2D 配列 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) が返されますが、半分の振幅 (つまりつまり、1 と -1 ではなく 0.5 と -0.5 の間で振動します) ! sin(2*y) または cos(2*y) (または sin(4*y), cos(4*y), ...) を使用すると問題なく動作することに注意してください。何か案が?

4

1 に答える 1

4

ここでの問題は、インプレースの実数から複素数への変換の入力と出力が、入力の実数データと同じサイズではない (2 倍の大きさの) 複雑な型であることです。実数から複素数への変換の中間複素数結果を保持するのに十分なメモリが割り当てられていません。ドキュメントからの引用:

cufftExecR2C() (cufftExecD2Z()) は、単精度 (倍精度) の実数から複素数への暗黙的な順方向の CUFFT 変換計画を実行します。CUFFT は、idata パラメーターが指す GPU メモリを入力データとして使用します。この関数は、非冗長フーリエ係数を odata 配列に格納します。idata と odata へのポインターは両方とも、単精度変換では cufftComplex データ型に整列し、倍精度変換では cufftDoubleComplex データ型に整列する必要があります。

解決策は、中間結果を保持するために 2 番目のデバイス バッファーを割り当てるか、その場での割り当てを拡大して、複雑なデータを保持するのに十分な大きさにすることです。したがって、コア変換コードは次のように変更されます。

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 行を追加するのは、それほど難しいことでしたか?

于 2013-10-06T10:55:25.130 に答える