копирование из GPU в CPU медленнее, чем копирование CPU в 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 integersfindNeiborElem <<< Nblocks, Nthreads >>>(dev_B, dev_MSH, dev_Nel, dev_Npel, dev_Nface, dev_FC);

Скопируйте ЦП снова

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

    __ 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,

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

Также я ожидаю, что время копирования одной и той же матрицы с процессора и на него будет одного порядка.

Ты хоть представляешь, что не так?

5

Решение

Вместо того, чтобы использовать 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

РЕДАКТИРОВАТЬ : Дополнительная информация :

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

3

Другие решения

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

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

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

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

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

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector