Почему использование cudaMemcpy2D для разделения / чередования сложных данных идет медленно - PullRequest
0 голосов
/ 09 сентября 2018

Я пишу мекс-код C / Cuda / Cublas и в версии Matlab до 2018 года, где сложные данные хранятся в разделенном формате. Однако подпрограммы cuBlas используют чередующийся формат. Таким образом, формат должен быть изменен с split на interleaved перед вызовом cuBlas, и interleaved для split перед возвратом в matlab.

Короче говоря, предположим, что double массивы Ar и Ai являются действительной и мнимой частями cuDoubleComplex массива A соответственно. Я хотел бы знать наиболее эффективный способ сделать следующие копии:

  1. копировать Ar и Ai в памяти хоста в A в памяти устройства.
  2. копирование A в памяти устройства в Ar и Ai в памяти хоста.

Первоначально я сделал что-то вроде следующего:

cudaDeviceSynchronize();
tic=getHighResolutionTime();

//Copy Ar and Ai into a A element by element, interleaving them in host memory. 
double2cuDoubleComplex(A,Ar,Ai,SIZE);

//Copy A to the device. A->d_A
cuDoubleComplex *dA;
cudaMalloc((void**)&d_A,sizeof(cuDoubleComplex)*SIZE);
cudaMemcpy(d_A,A, sizeof(cuDoubleComplex)*SIZE,cudaMemcpyHostToDevice);

/*** call cublas here overwriting d_A here ***/

//Copy the result from device back to host. d_A->A
cudaMemcpy(A,d_A,sizeof(cuDoubleComplex)*SIZE,cudaMemcpyDeviceToHost);

//Copy A into Ar and Ai element by element, splitting it in host memory.
cuDoubleComplex2double(A,Ar,Ai,SIZE);

cudaDeviceSynchronize();
toc=getHighResolutionTime();
printf("\n%f",toc-tic);

Вышеприведенное расточительно, потому что для чередования / разбиения массива требуется копирование элемента за элементом и требуется дополнительная временная переменная хоста. Поэтому вместо этого я попытался предложить использовать pud cudaMemcpy2D для разделения и чередования массивов во время передачи. Моя реализация была что-то вроде следующего:

cudaDeviceSynchronize();
tic=getHighResolutionTime();

//interleave the real and imag parts while copying the data to the device
double *d_A;
cudaMalloc((void**)&d_A,sizeof(double)*2*SIZE);
cudaMemcpy2D(d_A,2*sizeof(double),Ar,sizeof(double),sizeof(double),SIZE,cudaMemcpyHostToDevice);
cudaMemcpy2D(d_A+1,2*sizeof(double),Ai,sizeof(double),sizeof(double),SIZE,cudaMemcpyHostToDevice);
cuDoubleComplex *d_A=(cuDoubleComplex*)d_A;            

/*** call cublas here overwriting d_A here ***/

//split the interleaved complex data while copying it back from the device
d_A=(double*)d_A;    
cudaMemcpy2D(Ar,sizeof(double),d_A,2*sizeof(double),sizeof(double),SIZE,cudaMemcpyDeviceToHost);
cudaMemcpy2D(Ai,sizeof(double),d_A+1,2*sizeof(double),sizeof(double),SIZE,cudaMemcpyDeviceToHost);

cudaDeviceSynchronize();
toc=getHighResolutionTime();
printf("\n%f",toc-tic);

Однако второй метод даже менее эффективен (в два раза медленнее), чем первый! Я проверил это, комментируя вызовы cublas (как показано выше) и проверяя время для приведенных выше фрагментов. Я использовал большой SIZE=16800^2, чтобы обеспечить точность и рассчитал их несколько раз.

Объем данных, передаваемых между хостом и устройством, в обоих случаях одинаков. Почему второй метод медленнее, когда первый включает выделение временной переменной, а также копий элементов по элементам?

Я что-то не так делаю? Есть ли лучший способ сделать копии, которые я упоминал в начале поста?

...