如何将CUDA时钟周期转换为毫秒?

时间:2017-03-24 20:19:02

标签: time cuda

我想测量一下>内核中的一些代码的时间。我跟着this question及其注释,以便我的内核看起来像这样:

__global__ void kernel(..., long long int *runtime)
{
    long long int start = 0; 
    long long int stop = 0;

    asm volatile("mov.u64 %0, %%clock64;" : "=l"(start));

    /* Some code here */

    asm volatile("mov.u64 %0, %%clock64;" : "=l"(stop));

    runtime[threadIdx.x] = stop - start;
    ...
}

答案是按如下方式进行转换:

  

计时器计算时钟周期数。要获得毫秒数,请将其除以设备上的GHz数并乘以1000。

我为此做的:

for(long i = 0; i < size; i++)
{
  fprintf(stdout, "%d:%ld=%f(ms)\n", i,runtime[i], (runtime[i]/1.62)*1000.0);
}

其中1.62是我设备的GPU最大时钟频率。但是我以毫秒为单位的时间看起来不正确,因为它表明每个线程需要几分钟才能完成。这可能不正确,因为执行在不到一秒的挂钟时间内完成。转换公式不正确还是我在某处犯了错误?感谢。

2 个答案:

答案 0 :(得分:3)

您的情况下正确的转换不是GHz:

fprintf(stdout, "%d:%ld=%f(ms)\n", i,runtime[i], (runtime[i]/1.62)*1000.0);
                                                             ^^^^
但是赫兹:

fprintf(stdout, "%d:%ld=%f(ms)\n", i,runtime[i], (runtime[i]/1620000000.0f)*1000.0);
                                                             ^^^^^^^^^^^^^

在维度分析中:

                  clock cycles
clock cycles  /  -------------- = seconds
                   second

第一项是时钟周期测量。第二项是GPU的频率(以赫兹为单位,而不是GHz),第三项是所需的测量值(秒)。您可以通过将秒乘以1000来转换为毫秒。

这是一个有效的示例,显示了与设备无关的方法(因此您不必对时钟频率进行硬编码):

$ cat t1306.cu
#include <stdio.h>

const long long delay_time = 1000000000;
const int nthr = 1;
const int nTPB = 256;

__global__ void kernel(long long *clocks){

  int idx=threadIdx.x+blockDim.x*blockIdx.x;
  long long start=clock64();
  while (clock64() < start+delay_time);
  if (idx < nthr) clocks[idx] = clock64()-start;
}

int main(){

  int peak_clk = 1;
  int device = 0;
  long long *clock_data;
  long long *host_data;
  host_data = (long long *)malloc(nthr*sizeof(long long));
  cudaError_t err = cudaDeviceGetAttribute(&peak_clk, cudaDevAttrClockRate, device);
  if (err != cudaSuccess) {printf("cuda err: %d at line %d\n", (int)err, __LINE__); return 1;}
  err = cudaMalloc(&clock_data, nthr*sizeof(long long));
  if (err != cudaSuccess) {printf("cuda err: %d at line %d\n", (int)err, __LINE__); return 1;}
  kernel<<<(nthr+nTPB-1)/nTPB, nTPB>>>(clock_data);
  err = cudaMemcpy(host_data, clock_data, nthr*sizeof(long long), cudaMemcpyDeviceToHost);
  if (err != cudaSuccess) {printf("cuda err: %d at line %d\n", (int)err, __LINE__); return 1;}
  printf("delay clock cycles: %ld, measured clock cycles: %ld, peak clock rate: %dkHz, elapsed time: %fms\n", delay_time, host_data[0], peak_clk, host_data[0]/(float)peak_clk);
  return 0;
}
$ nvcc -arch=sm_35 -o t1306 t1306.cu
$ ./t1306
delay clock cycles: 1000000000, measured clock cycles: 1000000210, peak clock rate: 732000kHz, elapsed time: 1366.120483ms
$

这使用cudaDeviceGetAttribute来获取时钟速率,以kHz为单位返回结果,这使我们可以在这种情况下轻松计算毫秒数。

答案 1 :(得分:1)

clock64返回图形时钟周期中的值。图形时钟是动态的,因此我不建议您使用常量尝试转换为秒。如果您想转换为墙壁时间,那么更好的选择是使用globaltimer一个以纳秒为单位的64位时钟。

https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#special-registers-globaltimer

asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(start));

单位为纳秒。

默认分辨率为32ns,每µs更新一次。 NVIDIA性能工具将更新强制为每32 ns(或31.25 MHz)。当捕获并发内核跟踪时,CUPTI使用此时钟作为开始时间。