Синхронизация различных разделов в ядре 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 блоков, выполняющих одно и то же ядро, будет сохранена сумма всех их времен.
  • Сказав это, таймер предполагает, что активен нулевой поток, поэтому убедитесь, что вы не вызываете эти макросы в возможно расходящейся части кода.
  • Таймеры подсчитывают количество тактов часов. Чтобы получить количество миллисекунд, разделите его на количество ГГц на вашем устройстве и умножьте на 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