Сроки измерения при измерении ядра CUDA - PullRequest
0 голосов
/ 15 января 2019

Я пытаюсь измерить время, необходимое для функции ядра CUDA. Я измеряю время CPU и GPU. Но я получаю огромную разницу между ними.

Когда я профилирую его с помощью профилировщика NVIDIA, ядро ​​занимает около 6 мс, что я и хочу. Но когда я использовал gettimeofday () для вызова ядра для измерения времени процессора, мера была 15 мс. У меня там тоже нет вызовов memcpy. Ядро работает в отдельном потоке. И подобные ядра работают в параллельных потоках.

Пример кода:

gettimeofday(start);
cudaEventRecord(startGPU);

Kernel <<<abc, xyz,stream>>>();
cudaDeviceSynchronize();

cudaEventRecord(stopGPU);
printf("Elapsed GPU time  = ");

gettimeofday(stop);
printf("Elapsed CPU time  = ");

Результаты, которые я получаю для приведенного выше кода:

Истекшее время GPU = 6 мс Истекшее время процессора = 15 мс

Это странно, потому что присутствует только строка исполнения ядра. Однако параметры ядра являются указателями. Дополнительное время занято копиями мема? Но я не нахожу копии нигде в профиле. Любые выводы будут оценены.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

После отладки по той же проблеме, я обнаружил, что cuda обычно требует времени до первого запуска ядра, как указано на форуме здесь: https://devtalk.nvidia.com/default/topic/1042733/extremely-slow-cuda-api-calls-/?offset=3.

API времени выполнения cuda до ядра имели 6 мс cudaMalloc и 14 мс cudaLaunch, что стало причиной дополнительной задержки. Последующие ядра, однако, хорошо работали нормально. cudaLaunch обычно занимает время в микросекундах, поэтому, если что-то выходит за рамки этого, он определенно нуждается в ремонте.

ПРИМЕЧАНИЕ: Если вы запускаете какие-либо ядра cuda в цикле while (1) (только один раз), распределение должно выполняться вне цикла. Иначе у вас будут такие же задержки.

0 голосов
/ 15 января 2019

По сути, то, что вы измеряете, поскольку время вашего процессора - это время, которое требуется для

  1. записать первое событие,
  2. настроить запуск ядра с соответствующими параметрами,
  3. отправьте необходимые команды в графический процессор,
  4. запустить ядро ​​на GPU,
  5. запустить ядро ​​на GPU,
  6. дождаться уведомления о том, что выполнение графического процессора завершено, чтобы вернуться к ЦПУ, и
  7. записать второе событие.

Также обратите внимание, что ваш метод измерения времени ЦП измеряет не только время обработки, затраченное вашим процессом / потоком, а скорее общее системное время (которое потенциально включает время обработки, потраченное другими процессами / потоками, пока ваш процесс / поток не обязательно был даже запущен). Я должен признать, что даже в свете всего этого время процессора, о котором вы сообщаете, по-прежнему намного больше, чем время GPU, чем я обычно ожидаю. Но я не уверен, что это действительно весь твой код. На самом деле, я скорее сомневаюсь в этом, учитывая, что, например, printf() на самом деле ничего не печатает. Таким образом, могут быть некоторые дополнительные факторы, о которых мы не знаем, но которые все же необходимо учитывать, чтобы полностью объяснить ваше время.

В любом случае, скорее всего, ни одно из двух измерений, которые вы проводите, на самом деле не измеряет то, что вы действительно хотели измерить. Если вас интересует время, необходимое для запуска ядра, используйте события CUDA. Однако, если вы сначала синхронизируете и только потом записываете событие окончания, время между событиями начала и конца будет временем между началом выполнения ядра, процессором, ожидающим завершения выполнения ядра, и тем временем, которое может потребоваться для запишите второе событие и пусть оно попадет в GPU, чтобы вы могли спросить GPU, в какое время оно было получено. Думайте о событиях как о маркерах, которые отмечают определенную точку в потоке команд, отправляемом в графический процессор Скорее всего, вы действительно хотели написать это:

cudaEventRecord(startGPU, stream);       // mark start of kernel execution
Kernel<<<abc, xyz, stream>>>();
cudaEventRecord(stopGPU, stream);        // mark end of kernel execution
cudaEventSynchronize(stopGPU);   // wait for results to be available

и затем используйте cudaEventElapsedTime(), чтобы получить время между двумя событиями.

Также обратите внимание, что gettimeofday() является необязательно надежным способом получения времени с высоким разрешением. В C ++ вы можете использовать, например, std::steady_clock или std::high_resolution_clock (я бы прибегнул к последнему, только если этого нельзя избежать, так как это не гарантируется и убедитесь, что период времени действительно достаточен для того, что вы пытаетесь измерить).

...