Временно определяне на различни секции в ядрото на CUDA

Имам CUDA ядро, което извиква серия от функции на устройството.

Какъв е най-добрият начин да получите времето за изпълнение за всяка от функциите на устройството?

Кой е най-добрият начин да получите времето за изпълнение за част от кода в една от функциите на устройството?


person Roger Dahl    schedule 26.06.2012    source източник


Отговори (1)


В собствения си код използвам функцията clock(), за да получа точни времена. За удобство имам макросите

enum {
    tid_this = 0,
    tid_that,
    tid_count
    };
__device__ float cuda_timers[ tid_count ];
#ifdef USETIMERS
 #define TIMER_TIC clock_t tic; if ( threadIdx.x == 0 ) tic = clock();
 #define TIMER_TOC(tid) clock_t toc = clock(); if ( threadIdx.x == 0 ) atomicAdd( &cuda_timers[tid] , ( toc > tic ) ? (toc - tic) : ( toc + (0xffffffff - tic) ) );
#else
 #define TIMER_TIC
 #define TIMER_TOC(tid)
#endif

След това те могат да се използват за инструментиране на кода на устройството, както следва:

__global__ mykernel ( ... ) {

    /* Start the timer. */
    TIMER_TIC

    /* Do stuff. */
    ...

    /* Stop the timer and store the results to the "timer_this" counter. */
    TIMER_TOC( tid_this );

    }

След това можете да прочетете cuda_timers в кода на хоста.

Няколко бележки:

  • Таймерите работят на базата на блок, т.е. ако имате 100 блока, изпълняващи едно и също ядро, сумата от всичките им времена ще бъде съхранена.
  • Като каза това, таймерът приема, че нулевата нишка е активна, така че се уверете, че не извиквате тези макроси в евентуално различна част от кода.
  • Таймерите отчитат броя на тиктаканията на часовника. За да получите броя милисекунди, разделете това на броя GHz на вашето устройство и умножете по 1000.
  • Таймерите могат малко да забавят вашия код, поради което ги увих в #ifdef USETIMERS, за да можете лесно да ги изключите.
  • Въпреки че clock() връща целочислени стойности от тип clock_t, аз съхранявам натрупаните стойности като float, в противен случай стойностите ще се обвиват за ядра, които отнемат повече от няколко секунди (натрупани за всички блокове).
  • Изборът ( toc > tic ) ? (toc - tic) : ( toc + (0xffffffff - tic) ) ) е необходим в случай, че броячът на часовника се увива.

P.S. Това е копие от моя отговор на този въпрос, който не получи много точки там, тъй като необходимото време беше за цялото ядро.

person Pedro    schedule 26.06.2012
comment
Благодаря ти. Много полезно. Търсейки clock(), открих, че има и clock64(), което може да премахне необходимостта от проверка на препълване и преобразуване в плаващ. - person Roger Dahl; 26.06.2012
comment
@RogerDahl: Благодаря, че посочи това! Изглежда, че е добавен с CUDA 4.2. - person Pedro; 26.06.2012
comment
Ферми добави резултат от 64-битов часовник. Clock64 беше добавен доста преди CUDA 4.2. Обърнете внимание, че когато правите този тип синхронизиране, трябва да внимавате за разминаването -- ако различни изкривявания поемат по различни пътища в рамките на вашето синхронизиране, синхронизирането само на нишка 0 няма да бъде точно. - person harrism; 27.06.2012
comment
В допълнение към това, не забравяйте да разглобите изхода на компилатора и се уверете, че не е настъпило пренареждане. Компилаторът и асемблерът (поне по-старата open64 инструментална верига) могат и наистина преместват кода, това може да означава, че часовниковите извиквания могат да се окажат следващи, вместо да поставят в скоби кода, който сте възнамерявали. - person talonmies; 27.06.2012
comment
@harrism: Бях малко неточен за това. Функцията clock64() се появява само в Ръководството за програмиране на CUDA от версия 4.2. Що се отнася до първата ви точка, актуализирах съответно отговора си. Благодаря! - person Pedro; 27.06.2012
comment
@talonmies: Добра гледна точка. Ако се притеснявате за пренареждане, бихте могли да имплементирате макросите с помощта на вградено volatile асемблиране, напр. както е описано в долната част на страница 7 на Използване на PTX асемблиране в CUDA ръководство. - person Pedro; 27.06.2012