在对CUDA内核进行计时时,以下方法不起作用,因为内核在执行时不会阻止CPU程序执行:
start timer
kernel<<<g,b>>>();
end timer
我已经看到了(成功)计时CUDA内核的三种基本方法:
(1)两个CUDA eventRecords。
float responseTime; //result will be in milliseconds
cudaEvent_t start; cudaEventCreate(&start); cudaEventRecord(start); cudaEventSynchronize(start);
cudaEvent_t stop; cudaEventCreate(&stop);
kernel<<<g,b>>>();
cudaEventRecord(stop); cudaEventSynchronize(stop);
cudaEventElapsedTime(&responseTime, start, stop); //responseTime = elapsed time
(2)一个CUDA eventRecord。
float start = read_timer(); //helper function on CPU, in milliseconds
cudaEvent_t stop; cudaEventCreate(&stop);
kernel<<<g,b>>>();
cudaEventRecord(stop); cudaEventSynchronize(stop);
float responseTime = read_timer() - start;
(3)deviceSynchronize而不是eventRecord。 (可能仅在单个流中使用编程时才有用。)
float start = read_timer(); //helper function on CPU, in milliseconds
kernel<<<g,b>>>();
cudaDeviceSynchronize();
float responseTime = read_timer() - start;
我通过实验证实这三种策略产生相同的时间结果。
问题:
cudaEventElapsedTime()
函数有什么好处吗? 你可以用你的想象力来弄清楚read_timer()
的作用。然而,提供示例实现并不会有害:
double read_timer(){
struct timeval start;
gettimeofday( &start, NULL ); //you need to include <sys/time.h>
return (double)((start.tv_sec) + 1.0e-6 * (start.tv_usec))*1000; //milliseconds
}
答案 0 :(得分:1)
你似乎已经排除了大部分差异,说它们都会为你所展示的相对简单的情况产生相同的结果(可能不完全正确,但我理解你的意思),以及“除了时间(复杂)序列)......“第一个案例显然更好。
一个可能的区别是windows和linux之间的可移植性。我相信你的示例read_timer函数是面向linux的。您可以制作一个“便携式”的read_timer函数,但cuda事件系统(方法1)可以原样移植。
答案 1 :(得分:0)
选项(1)使用cudaEventRecord为CPU计时。这是非常低效的,我不鼓励使用cudaEventRecord来达到这个目的。 cudaEventRecord可用于计算GPU推送缓冲时间以执行内核,如下所示:
float responseTime; //result will be in milliseconds
cudaEvent_t start;
cudaEvent_t stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
kernel<<<g,b>>>();
cudaEventRecord(stop);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&responseTime, start, stop); //responseTime = elapsed time
如果您向多个流提交多个工作项,则需要稍微更改代码。我建议阅读Difference in time reported by NVVP and counters
的答案选项(2)和(3)对于给定的例子是类似的。选项(2)可以更灵活。