什么是C / C ++程序员可以使用的最快的计时系统?
例如:
time()会给出自1970年1月1日00:00以来的秒数。
Windows上的GetTickCount()会自系统启动时间以毫秒为单位给出时间,但限于49.7天(之后它简单回绕为零)。
我想获取当前时间,或者从系统/应用程序启动时间(以毫秒为单位)开始计时。
最大的担忧是该方法的开销 – 我需要最轻的一个,因为我打算每秒钟多次调用它。
我的情况是,我有一个工作线程,并为该工作线程我张贴待工作。 每个工作都有一个“执行时间”。 所以,我不在乎时间是否是当前的“真实”时间或系统运行时间以来的时间 – 它必须是线性的和轻的。
编辑:
unsigned __int64 GetTickCountEx() { static DWORD dwWraps = 0; static DWORD dwLast = 0; DWORD dwCurrent = 0; timeMutex.lock(); dwCurrent = GetTickCount(); if(dwLast > dwCurrent) dwWraps++; dwLast = dwCurrent; unsigned __int64 timeResult = ((unsigned __int64)0xFFFFFFFF * dwWraps) + dwCurrent; timeMutex.unlock(); return timeResult; }
对于时间安排, 目前的微软建议使用QueryPerformanceCounter
& QueryPerformanceFrequency
。
这会给你比毫秒更好的时间。 如果系统不支持高分辨率计时器,则默认为毫秒(与GetTickCount
相同)。
这里是一个简短的Microsoft文章,为什么你应该使用它的例子 🙂
我最近有这个问题,做了一些研究。 好消息是所有三个主要的操作系统都提供了某种高分辨率的定时器。 坏消息是它是在每个系统上的不同的API调用。 对于要使用clock_gettime()的POSIX操作系统。 但是,如果您使用的是Mac OS X,则不支持此操作,则必须使用mach_get_time()。 对于Windows,使用QueryPerformanceCounter。 或者,使用支持OpenMP的编译器,可以使用omp_get_wtime(),但它可能不提供您正在查找的解决方案。
我也从fftw.org(www.fftw.org/cycle.h)找到了cycle.h。
这是一些在每个操作系统上调用一个定时器的代码,使用一些丑陋的#ifdef语句。 用法很简单:定时器t; t.tic(); SomeOperation(); t.toc( “消息”); 它将以秒为单位打印经过的时间。
#ifndef TIMER_H #define TIMER_H #include <iostream> #include <string> #include <vector> # if (defined(__MACH__) && defined(__APPLE__)) # define _MAC # elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64)) # define _WINDOWS # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif #endif # if defined(_MAC) # include <mach/mach_time.h> # elif defined(_WINDOWS) # include <windows.h> # else # include <time.h> # endif #if defined(_MAC) typedef uint64_t timer_t; typedef double timer_c; #elif defined(_WINDOWS) typedef LONGLONG timer_t; typedef LARGE_INTEGER timer_c; #else typedef double timer_t; typedef timespec timer_c; #endif //============================================================================== // Timer // A quick class to do benchmarking. // Example: Timer t; t.tic(); SomeSlowOp(); t.toc("Some Message"); class Timer { public: Timer(); inline void tic(); inline void toc(); inline void toc(const std::string &msg); void print(const std::string &msg); void print(); void reset(); double getTime(); private: timer_t start; double duration; timer_c ts; double conv_factor; double elapsed_time; }; Timer::Timer() { #if defined(_MAC) mach_timebase_info_data_t info; mach_timebase_info(&info); conv_factor = (static_cast<double>(info.numer))/ (static_cast<double>(info.denom)); conv_factor = conv_factor*1.0e-9; #elif defined(_WINDOWS) timer_c freq; QueryPerformanceFrequency(&freq); conv_factor = 1.0/(static_cast<double>freq.QuadPart); #else conv_factor = 1.0; #endif reset(); } inline void Timer::tic() { #if defined(_MAC) start = mach_absolute_time(); #elif defined(_WINDOWS) QueryPerformanceCounter(&ts); start = ts.QuadPart; #else clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); start = static_cast<double>(ts.tv_sec) + 1.0e-9 * static_cast<double>(ts.tv_nsec); #endif } inline void Timer::toc() { #if defined(_MAC) duration = static_cast<double>(mach_absolute_time() - start); #elif defined(_WINDOWS) QueryPerformanceCounter(&qpc_t); duration = static_cast<double>(qpc_t.QuadPart - start); #else clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); duration = (static_cast<double>(ts.tv_sec) + 1.0e-9 * static_cast<double>(ts.tv_nsec)) - start; #endif elapsed_time = duration*conv_factor; } inline void Timer::toc(const std::string &msg) { toc(); print(msg); }; void Timer::print(const std::string &msg) { std::cout << msg << " "; print(); } void Timer::print() { if(elapsed_time) { std::cout << "elapsed time: " << elapsed_time << " seconds\n"; } } void Timer::reset() { start = 0; duration = 0; elapsed_time = 0; } double Timer::getTime() { return elapsed_time; } #if defined(_WINDOWS) # undef WIN32_LEAN_AND_MEAN #endif #endif // TIMER_H
GetSystemTimeAsFileTime是最快的资源。 它的粒度可以通过调用GetSystemTimeAdjustment(调用lpTimeIncrement)来获得。 系统时间作为文件时间有100ns单位和增量TimeIncrement 。 TimeIncrement可以改变,这取决于多媒体定时器接口的设置。
调用timeGetDevCaps将公开时间服务的功能。 它为最小支持的中断周期返回一个值wPeriodMin 。 以wPeriodMin作为参数的timeBeginPeriod调用将使系统以最高可能的中断频率(通常约1ms)运行。 这也会强制GetSystemTimeAsFileTime
返回的系统文件时间的增量更小。 其粒度范围为1ms(10000个100ns单位)。
为了你的目的,我建议去这个方法。
QueryPerformanceCounter的选择是有问题的,因为它的频率不准确的方法有两种:首先它偏离了由QueryPerformanceFrequency给出的硬件特定的偏移量。 这个偏移量可以轻松地达到几百ppm,这意味着转换成时间将包含每秒几百微秒的误差。 其次它有热漂移。 这些设备的漂移可以轻松地达到几个ppm。 这样,另一个 – 热依赖 – 几个我们的错误被添加。
所以只要〜1ms的分辨率就足够了,主要的问题是开销, GetSystemTimeAsFileTime
是迄今为止最好的解决方案。
微秒重要时,你必须走更长的路,看到更多的细节。 亚秒级时间服务在Windows时间戳项目中描述
如果你只是担心GetTickCount()
溢出,那么你可以像这样包装它:
DWORDLONG GetLongTickCount(void) { static DWORDLONG last_tick = 0; DWORD tick = GetTickCount(); if (tick < (last_tick & 0xffffffff)) last_tick += 0x100000000; last_tick = (last_tick & 0xffffffff00000000) | tick; return last_tick; }
如果你想从多个线程调用这个,你需要锁定对last_tick
变量的访问。 只要每49.7天至少调用一次GetLongTickCount()
,就会检测到溢出。
如果您专门针对Windows,建议您使用GetSystemTimeAsFileTime API。 它通常比GetSystemTime快,并具有相同的精度(大约10-15毫秒 – 不要看分辨率); 几年前我在Windows XP下做了一个基准测试,结果是在50-100倍的速度范围内。
唯一的缺点是,如果您需要以更人性化的格式访问返回的时间,则可能必须使用FileTimeToSystemTime将返回的FILETIME结构转换为时钟时间。 另一方面,只要你不需要实时转换的时间,你就可以总是以离线或“懒惰”的方式来做到这一点(例如只转换你需要显示/处理的时间戳,只有当你真的需要他们)。
QueryPerformanceCounter可能是其他人提到的一个很好的选择,但是开销可能相当大,具体取决于底层的硬件支持。 在我的基准测试中,我提到上面的QueryPerformanceCounter调用比GetSystemTimeAsFileTime调用慢了25-200倍。 另外,这里还有一些可靠性问题。
所以,总之:如果你能应付10-15毫秒的精度,我建议你使用GetSystemTimeAsFileTime。 如果你需要比QueryPerformanceCounter更好的东西。
小小的免责声明:在Windows XP以后的版本中,我还没有比XP SP3做任何基准测试。 我建议你自己做一些基准测试。
在Linux上,你得到微秒:
struct timeval tv; int res = gettimeofday(&tv, NULL); double tmp = (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec;
在Windows上,只有毫秒可用:
SYSTEMTIME st; GetSystemTime(&st); tmp += 1e-3 * st.wMilliseconds; return tmp;
这是来自R的datetime.c(为简洁起见,编辑了下来)。
那么当然Boost的Date_Time可以在某些系统上具有纳秒分辨率(细节在这里和这里 )。
如果你的目标版本足够晚,那么你可以使用GetTickCount64()
,它比GetTickCount()
要高得多。 你也可以简单地在GetTickCount64()
之上构建一个GetTickCount64()
版本。
您是否查看过此MSDN文章中的代码?
http://msdn.microsoft.com/en-us/magazine/cc163996.aspx
我有这个代码在使用VC2005和C ++ Builder XE的Windows 7 64位机器上编译,但是执行时会锁定我的机器; 还没有调试到足以找出为什么呢。 看起来过于复杂。 UG模板模板
在Mac OS X上,您可以简单地使用UInt32 TickCount(void)来获取刻度。
POSIX支持使用具有纳秒分辨率的struct timespec
clock_gettime() 。 无论你的系统是否真的支持细粒度的解决方案,都是值得商榷的,但我相信这是最高分辨率的标准调用。 并不是所有的系统都支持它,而且有时候它隐藏得很好(Solaris,IIRC上的库' -lposix4
')。
更新(2016-09-20):
clock_gettime()
,Mac OS X 10.11.6 El Capitan也没有任何其他版本的Mac OS X,但macOS Sierra 10.12的确具有clock_gettime()
和手册页为最后。 实际分辨率(在CLOCK_MONOTONIC
)仍然是微秒; 较小的单位全是零。 这由clock_getres()
来确认,它报告分辨率是1000纳秒,也就是1微秒。 macOS Sierra上clock_gettime()
的手册页提到了mach_absolute_time()
作为获取高分辨率时序的方法。 有关更多信息,请参阅技术问答QA1398:Mach绝对时间单位和(在SO上mach_absolute_time()
基于iPhone的mach_absolute_time()
是什么?