Я пишу мекс-код C / Cuda / Cublas и в версии Matlab до 2018 года, где сложные данные хранятся в разделенном формате. Однако подпрограммы cuBlas используют чередующийся формат. Таким образом, формат должен быть изменен с split на interleaved перед вызовом cuBlas, и interleaved для split перед возвратом в matlab.
Короче говоря, предположим, что double
массивы Ar
и Ai
являются действительной и мнимой частями cuDoubleComplex
массива A
соответственно. Я хотел бы знать наиболее эффективный способ сделать следующие копии:
- копировать
Ar
и Ai
в памяти хоста в A
в памяти устройства.
- копирование
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
, чтобы обеспечить точность и рассчитал их несколько раз.
Объем данных, передаваемых между хостом и устройством, в обоих случаях одинаков. Почему второй метод медленнее, когда первый включает выделение временной переменной, а также копий элементов по элементам?
Я что-то не так делаю? Есть ли лучший способ сделать копии, которые я упоминал в начале поста?