¿Por qué es tan lento cuFFT?

Espero acelerar una aplicación de visión artificial que computa muchos FFT usando FFTW y OpenMP en una CPU Intel. Sin embargo, para una variedad de tamaños de problemas de FFT, he encontrado que cuFFT es más lento que FFTW con OpenMP.

En los experimentos y la discusión a continuación, encuentro que cuFFT esmás lento que FFTW para FFT en 2D por lotes.¿Por qué es tan lento el cuFFT y hay algo que pueda hacer para que el cuFFT se ejecute más rápido?

Experimentos (descarga de código)

Nuestroaplicación de visión artificial requiere un FFT adelantado en un montón de planos pequeños de tamaño 256x256. Estoy ejecutando los FFT enCERDO funciones con una profundidad de 32, por lo que utilizo el modo por lotes para realizar 32 FFT por llamada de función. Por lo general, hago aproximadamente 8 llamadas de función FFT de tamaño 256x256 con un tamaño de lote de 32.

FFTW + OpenMP
El siguiente código se ejecuta en16.0ms en unaIntel i7-2600 8-core CPU.

int depth = 32; int nRows = 256; int nCols = 256; int nIter = 8;
int n[2] = {nRows, nCols};

//if nCols is even, cols_padded = (nCols+2). if nCols is odd, cols_padded = (nCols+1)
int cols_padded = 2*(nCols/2 + 1); //allocate this width, but tell FFTW that it's nCols width
int inembed[2] = {nRows, 2*(nCols/2 + 1)};
int onembed[2] = {nRows, (nCols/2 + 1)}; //default -- equivalent ot onembed=NULL

float* h_in = (float*)malloc(sizeof(float)*nRows*cols_padded*depth);
memset(h_in, 0, sizeof(float)*nRows*cols_padded*depth);
fftwf_complex* h_freq = reinterpret_cast<fftwf_complex*>(h_in); //in-place version

fftwf_plan forwardPlan = fftwf_plan_many_dft_r2c(2, //rank
                                                 n, //dims -- this doesn't include zero-padding
                                                 depth, //howmany
                                                 h_in, //in
                                                 inembed, //inembed
                                                 depth, //istride
                                                 1, //idist
                                                 h_freq, //out
                                                 onembed, //onembed
                                                 depth, //ostride
                                                 1, //odist
                                                 FFTW_PATIENT /*flags*/);
double start = read_timer();
#pragma omp parallel for
for(int i=0; i<nIter; i++){
    fftwf_execute_dft_r2c(forwardPlan, h_in, h_freq);
}
double responseTime = read_timer() - start;
printf("did %d FFT calls in %f ms \n", nIter, responseTime);


cuFFT
El siguiente código se ejecuta en21.7ms en un top-of-the-lineNVIDIA K20 GPU. Tenga en cuenta que, incluso si uso secuencias,cuFFT haceno Ejecutar múltiples FFT al mismo tiempo.

int depth = 32; int nRows = 256; int nCols = 256; int nIter = 8;
int n[2] = {nRows, nCols};

int cols_padded = 2*(nCols/2 + 1); //allocate this width, but tell FFTW that it's nCols width
int inembed[2] = {nRows, 2*(nCols/2 + 1)};
int onembed[2] = {nRows, (nCols/2 + 1)}; //default -- equivalent ot onembed=NULL in FFTW
cufftHandle forwardPlan;
float* d_in; cufftComplex* d_freq;
CHECK_CUFFT(cufftPlanMany(&forwardPlan,
              2, //rank
              n, //dimensions = {nRows, nCols}
              inembed, //inembed
              depth, //istride
              1, //idist
              onembed, //onembed
              depth, //ostride
              1, //odist
              CUFFT_R2C, //cufftType
              depth /*batch*/));

CHECK_CUDART(cudaMalloc(&d_in, sizeof(float)*nRows*cols_padded*depth));
d_freq = reinterpret_cast<cufftComplex*>(d_in);

double start = read_timer();
for(int i=0; i<nIter; i++){

    CHECK_CUFFT(cufftExecR2C(forwardPlan, d_in, d_freq));
}
CHECK_CUDART(cudaDeviceSynchronize());
double responseTime = read_timer() - start;
printf("did %d FFT calls in %f ms \n", nIter, responseTime);

Otras notas

En la versión GPU,cudaMemcpys entre la CPU y la GPU sonno incluido en mi tiempo de computación.Los números de rendimiento presentados aquí son promedios de varios experimentos, donde cada experimento tiene 8 llamadas de función FFT (un total de 10 experimentos, por lo que 80 llamadas de función FFT).He intentado muchos tamaños de problemas (por ejemplo, 128x128, 256x256, 512x512, 1024x1024), todos con profundidad = 32. Basado en elnvvp generador de perfiles, algunos tamaños como 1024x1024 pueden saturar completamente la GPU. Pero, para todos estos tamaños, la CPU FFTW + OpenMP es más rápida que cuFFT.

Respuestas a la pregunta(1)

Su respuesta a la pregunta