我目前有三个测量经过时间的方法,两个使用CUDA事件,另一个记录开始和结束UNIX。那些使用CUDA事件的人测量两件事,一个测量整个外部循环时间,另一个测量所有内核执行时间。
以下是代码:
int64 x1, x2;
cudaEvent_t start;
cudaEvent_t end;
cudaEvent_t s1, s2;
float timeValue;
#define timer_s cudaEventRecord(start, 0);
#define timer_e cudaEventRecord(end, 0); cudaEventSynchronize(end); cudaEventElapsedTime( &timeValue, start, end ); printf("time: %f ms \n", timeValue);
cudaEventCreate( &start );
cudaEventCreate( &end );
cudaEventCreate( &s1 );
cudaEventCreate( &s2 );
cudaEventRecord(s1, 0);
x1 = GetTimeMs64();
for(int r = 0 ; r < 2 ; r++)
{
timer_s
kernel1<<<1, x>>>(gl_devdata_ptr);
cudaThreadSynchronize();
timer_e
sum += timeValue;
for(int j = 0 ; j < 5; j++)
{
timer_s
kernel2<<<1,x>>>(gl_devdata_ptr);
cudaThreadSynchronize();
timer_e
sum += timeValue;
timer_s
kernel3<<<1,x>>>(gl_devdata_ptr);
cudaThreadSynchronize();
timer_e
sum += timeValue;
}
timer_s
kernel4<<<y, x>>> (gl_devdata_ptr);
cudaThreadSynchronize();
timer_e
sum += timeValue;
}
x2 = GetTimeMs64();
cudaEventRecord(s2, 0);
cudaEventSynchronize(s2);
cudaEventElapsedTime( &timeValue, s1, s2 );
printf("elapsed cuda : %f ms \n", timeValue);
printf("elapsed sum : %f ms \n", sum);
printf("elapsed win : %d ms \n", x2-x1);
GetTimeMs64是我在StackOverflow上找到的东西:
int64 GetTimeMs64()
{
/* Windows */
FILETIME ft;
LARGE_INTEGER li;
uint64 ret;
/* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
* to a LARGE_INTEGER structure. */
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
ret = li.QuadPart;
ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */
return ret;
}
那些不是真正的变量名称,也不是正确的内核名称,我只是删除了一些以使代码更小。
所以问题是,每一项措施都给我一个非常不同的总时间。
我刚跑过的一些例子:
elapsed cuda : 21.076832
elapsed sum : 4.177984
elapsed win : 27
那为什么会有这么大的差异呢?所有内核调用的总和大约是4毫秒,其他18毫秒呢? CPU时间?
答案 0 :(得分:0)
cudaThreadSynchronize是一个非常高的开销操作,因为它必须等待GPU上的所有工作完成。
如果按如下方式构建代码,则应该得到正确的结果:
int64 x1, x2;
cudaEvent_t start;
cudaEvent_t end;
const int k_maxEvents = 5 + (2 * 2) + (2 * 5 * 2);
cudaEvent_t events[k_maxEvents];
int eIdx = 0;
float timeValue;
for (int e = 0; e < 5; ++e)
{
cudaEventCreate(&events[e]);
}
x1 = GetTimeMs64();
cudaEventRecord(events[eIdx++], 0);
for(int r = 0 ; r < 2 ; r++)
{
cudaEventRecord(events[eIdx++], 0);
kernel1<<<1, x>>>(gl_devdata_ptr);
for(int j = 0 ; j < 5; j++)
{
cudaEventRecord(events[eIdx++], 0);
kernel2<<<1,x>>>(gl_devdata_ptr);
cudaEventRecord(events[eIdx++], 0);
kernel3<<<1,x>>>(gl_devdata_ptr);
}
cudaEventRecord(events[eIdx++], 0);
kernel4<<<y, x>>> (gl_devdata_ptr);
}
cudaEventRecord(eIdx++, 0);
cudaDeviceSynchronize();
x2 = GetTimeMs64();
cudaEventElapsedTime( &timeValue, events[0], events[k_maxEvents - 1] );
printf("elapsed cuda : %f ms \n", timeValue);
// TODO the time between each events is the time to execute each kernel.
// On WDDM a context switch may occur between any of the kernels leading
// to higher than expected results.
// printf("elapsed sum : %f ms \n", sum);
printf("elapsed win : %d ms \n", x2-x1);
在Windows上,一种更容易测量时间的方法是使用QueryPerformanceCounter和QueryPerformanceFrequency。
如果您编写上述示例而没有事件
#include "NvToolsExt.h"
nvtxRangePushA("CPU Time");
for(int r = 0 ; r < 2 ; r++)
{
kernel1<<<1, x>>>(gl_devdata_ptr);
for(int j = 0 ; j < 5; j++)
{
kernel2<<<1,x>>>(gl_devdata_ptr);
kernel3<<<1,x>>>(gl_devdata_ptr);
}
kernel4<<<y, x>>> (gl_devdata_ptr);
}
cudaDeviceSynchronize();
nvtxRangePop();
并在Nsight Visual Studio Edition 1.5-2.2中运行CUDA跟踪活动或Visual Profiler 4.0+所有时间都可用。 GPU时间将比使用cudaEvents API收集的时间更准确。使用nvtxRangePush来测量CPU时间范围是可选的。这也可以通过从示例中的第一个CUDA API到cudaDeviceSynchronize结束进行测量来实现。