копирование с GPU на процессор медленнее, чем копирование процессора на GPU

Я начал изучать cuda на некоторое время, и у меня есть следующая проблема

Посмотрите, как я делаю ниже:

Копировать GPU

int* B; // ... int *dev_B; //initialize B=0 cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int)); cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice); //... //Execute on GPU the following function which is supposed to fill in //the dev_B matrix with integers findNeiborElem <<>>(dev_B, dev_MSH, dev_Nel, dev_Npel, dev_Nface, dev_FC); 

Скопируйте CPU еще раз

 cudaMemcpy(B, dev_B, Nel*Nface*sizeof(int),cudaMemcpyDeviceToHost); 
  1. Копирование массива B в dev_B занимает всего лишь часть секунды. Однако копирование массива dev_B обратно в B выполняется навсегда.
  2. Функция findNeiborElem включает цикл для каждого streamа, например, он выглядит так

     __ global __ void findNeiborElem(int *dev_B, int *dev_MSH, int *dev_Nel, int *dev_Npel, int *dev_Nface, int *dev_FC){ int tid=threadIdx.x + blockIdx.x * blockDim.x; while (tid<dev_Nel[0]){ for (int j=1;j<=Nel;j++){ // do some calculations B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach break; } tid += blockDim.x * gridDim.x; } } 

Что очень странно в том, что время для копирования dev_B в B пропорционально числу итераций индекса j.

Например, если Nel=5 то время составляет около 5 sec .

Когда я увеличиваю Nel=20 время составляет около 20 sec .

Я ожидал бы, что время копирования должно быть независимым от внутренних итераций, нужно назначить значение Matrix dev_B .

Также я ожидал бы, что время для копирования одной и той же матрицы из и в CPU будет иметь тот же порядок.

Вы знаете, что не так?

Вместо использования clock () для измерения времени вы должны использовать события:

С событиями у вас будет что-то вроде этого:

  cudaEvent_t start, stop; // variables that holds 2 events float time; // Variable that will hold the time cudaEventCreate(&start); // creating the event 1 cudaEventCreate(&stop); // creating the event 2 cudaEventRecord(start, 0); // start measuring the time // What you want to measure cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int)); cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice); cudaEventRecord(stop, 0); // Stop time measuring cudaEventSynchronize(stop); // Wait until the completion of all device // work preceding the most recent call to cudaEventRecord() cudaEventElapsedTime(&time, start, stop); // Saving the time measured 

EDIT : Дополнительная информация:

«Ядро запускает управление streamом процессора до его завершения, поэтому ваша временная конструкция измеряет время выполнения ядра, а также вторую memcpy. При синхронизации копии после ядра ваш код таймера запускается немедленно, но cudaMemcpy ожидает завершения ядра до его запуска. Это также объясняет, почему ваше измерение времени для возврата данных, по-видимому, зависит от итераций цикла ядра. Это также объясняет, почему время, затрачиваемое на вашу функцию ядра, «ничтожно». кредиты Роберту Кровели

Что касается вашего второго вопроса

  B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach 

При выполнении вычислений на графическом процессоре из-за причин синхронизации каждый stream, завершивший свое задание, не выполняет никаких вычислений до тех пор, пока не завершится весь stream в той же рабочей группе.

Другими словами, время, необходимое для выполнения этого расчета, будет в худшем случае, неважно, если большая часть streamов не пройдет полностью.

Я не уверен в вашем первом вопросе, как вы измеряете время? Я не слишком знаком с cuda, но я думаю, что при копировании с CPU на GPU реализация блокирует ваши данные, скрывая эффективное время.