为什么以下程序的相同 cufft 代码需要不同的时间?

Why the same cufft code of the following program takes different amount of time?

我运行 cufft (cuda 9) (Nvidia 1080) 中的以下代码。所有执行的代码都是相同的。但是,执行时间(在代码下方)变化很大。任何人都可以描述如何始终获得最短时间以及这种行为背后的原因吗?

int NX 2048
int BATCH 96

cufftHandle plan;
cufftHandle rev_plan;
cufftDoubleReal *idata;
cufftDoubleComplex *odata;

int BLOCKSIZE  = 1024;
int gridSize = (NX * BATCH)/BLOCKSIZE;

cufftPlan1d(&plan, NX, CUFFT_D2Z, BATCH);
cufftPlan1d(&rev_plan, NX, CUFFT_Z2D, BATCH);


cudaMalloc((void **) &idata, sizeof(cufftDoubleReal) * NX * BATCH);
cudaMalloc((void **) &odata, sizeof(cufftDoubleComplex) * (NX / 2 + 1) * BATCH);
inputData << < gridSize, BLOCKSIZE >> > (idata, NX * BATCH);

double sT = omp_get_wtime();
for (int i = 0; i < 500; ++i) {
    cufftExecD2Z(plan, idata, odata);
    cufftExecZ2D(plan, odata, idata);
}
printf("Time taken: %f\n", omp_get_wtime() - sT);

sT = omp_get_wtime();
for (int i = 0; i < 500; ++i) {
    cufftExecD2Z(plan, idata, odata);
    cufftExecZ2D(plan, odata, idata);
}
printf("Time taken: %f\n", omp_get_wtime() - sT);

sT = omp_get_wtime();
for (int i = 0; i < 500; ++i) {
    cufftExecD2Z(plan, idata, odata);
    cufftExecZ2D(plan, odata, idata);
}
printf("Time taken: %f\n", omp_get_wtime() - sT);

sT = omp_get_wtime();
for (int i = 0; i < 500; ++i) {
    cufftExecD2Z(plan, idata, odata);
    cufftExecZ2D(plan, odata, idata);
}
printf("Time taken: %f\n", omp_get_wtime() - sT);

cudaFree(idata);
cudaFree(odata);

耗时:0.004334 所用时间:0.022906 所用时间:0.027820 耗时:0.027786

cufft 例程的调用可以是异步

这意味着调用可能 return 在工作完成之前

这只能在一定限度内发生。有一个异步启动队列。一旦填满队列,队列中的新插槽只会在调度队列项时打开。这意味着启动过程不再是异步的。

这会扭曲您的计时结果。

为了 "fix" 这个,在每个计时区域结束前添加一个 cudaDeviceSynchronize(); 调用(即紧接在每个 printf 语句之前)。这将大大平衡结果。这会强制所有 GPU 工作在您完成计时测量之前完成。

$ cat t37.cu
#include <cufft.h>
#include <omp.h>
#include <cuda_runtime_api.h>
#include <cstdio>

int main(){

  const int NX = 2048;
  const int BATCH = 96;

  cufftHandle plan;
  cufftHandle rev_plan;
  cufftDoubleReal *idata;
  cufftDoubleComplex *odata;

  //int BLOCKSIZE  = 1024;
  //int gridSize = (NX * BATCH)/BLOCKSIZE;

  cufftPlan1d(&plan, NX, CUFFT_D2Z, BATCH);
  cufftPlan1d(&rev_plan, NX, CUFFT_Z2D, BATCH);


  cudaMalloc((void **) &idata, sizeof(cufftDoubleReal) * NX * BATCH);
  cudaMalloc((void **) &odata, sizeof(cufftDoubleComplex) * (NX / 2 + 1) * BATCH);
  //inputData << < gridSize, BLOCKSIZE >> > (idata, NX * BATCH);

  double sT = omp_get_wtime();
  for (int i = 0; i < 500; ++i) {
            cufftExecD2Z(plan, idata, odata);
            cufftExecZ2D(plan, odata, idata);
  }
  #ifdef FIX
  cudaDeviceSynchronize();
  #endif
  printf("Time taken: %f\n", omp_get_wtime() - sT);

  sT = omp_get_wtime();
  for (int i = 0; i < 500; ++i) {
            cufftExecD2Z(plan, idata, odata);
            cufftExecZ2D(plan, odata, idata);
  }
  #ifdef FIX
  cudaDeviceSynchronize();
  #endif
  printf("Time taken: %f\n", omp_get_wtime() - sT);

  sT = omp_get_wtime();
  for (int i = 0; i < 500; ++i) {
            cufftExecD2Z(plan, idata, odata);
            cufftExecZ2D(plan, odata, idata);
  }
  #ifdef FIX
  cudaDeviceSynchronize();
  #endif
  printf("Time taken: %f\n", omp_get_wtime() - sT);

  sT = omp_get_wtime();
  for (int i = 0; i < 500; ++i) {
            cufftExecD2Z(plan, idata, odata);
            cufftExecZ2D(plan, odata, idata);
  }
  #ifdef FIX
  cudaDeviceSynchronize();
  #endif
  printf("Time taken: %f\n", omp_get_wtime() - sT);

  cudaFree(idata);
  cudaFree(odata);
}
$ nvcc -o t37 t37.cu -lcufft -lgomp
$ ./t37
Time taken: 0.007373
Time taken: 0.185308
Time taken: 0.196998
Time taken: 0.196857
$ nvcc -o t37 t37.cu -lcufft -lgomp -DFIX
$ ./t37
Time taken: 0.197076
Time taken: 0.196994
Time taken: 0.196937
Time taken: 0.196916
$

有人会问,"why is the total time without the cudaDeviceSynchronize() call apparently lower than the total time with it?"这其实也是同一个原因。异步启动队列充满了待处理的工作,但程序在队列中的所有工作启动之前终止(没有 final cudaDeviceSynchronize())。在每种情况下,这都会导致总执行时间之间出现明显差异。通过仅添加最后一个 cudaDeviceSynchronize() 调用,可以观察到这种效果。