C ++跨平台高分辨率定时器
我期待在C ++中实现一个简单的计时器机制。 该代码应该在Windows和Linux中工作。 分辨率应尽可能精确(至less精确到毫秒)。 这将被用来简单地跟踪时间的stream逝,而不是实现任何事件驱动的devise。 什么是最好的工具来完成这个?
对于C ++ 03 :
Boost.Timer可能会工作,但它取决于C函数clock
,所以可能没有足够的分辨率给你。
Boost.Date_Time包含一个在Stack Overflow之前推荐的ptime
类 。 查看它的文档microsec_clock::local_time
和microsec_clock::universal_time
,但注意它的警告:“Win32系统通常不能通过这个API实现微秒的分辨率。
除此之外 , STLsoft还提供了针对特定于操作系统的API的跨平台(Windows和Linux / Unix)精简C ++包装器。 它的性能库有几个类可以做你需要的。 (为了使其跨平台,请select类似于winstl
和unixstl
名称空间中存在的winstl
unixstl
,然后使用与您的平台匹配的名称空间。)
对于C ++ 11及以上版本 :
std::chrono
库内置了这个function。详细请看@HowardHinnant的答案 。
更新了旧问题的答案:
在C ++ 11中,您可以轻松地获得最高分辨率的计时器:
#include <iostream> #include <chrono> #include "chrono_io" int main() { typedef std::chrono::high_resolution_clock Clock; auto t1 = Clock::now(); auto t2 = Clock::now(); std::cout << t2-t1 << '\n'; }
示例输出:
74 nanoseconds
“chrono_io”是一个扩展,以缓解这些新types的I / O问题,并可在此免费获取。
在boost中还有一个<chrono>
的实现(可能仍然在主干上,不确定它是否被释放)。
更新
这是为了回应Ben的评论,随后对std::chrono::high_resolution_clock
调用在VS11中需要几个毫秒。 下面是一个<chrono>
兼容的解决方法。 然而,它只能在英特尔硬件上工作,您需要深入内联汇编(根据编译器不同,语法不同),并且必须将机器的时钟速度硬连线到时钟:
#include <chrono> struct clock { typedef unsigned long long rep; typedef std::ratio<1, 2800000000> period; // My machine is 2.8 GHz typedef std::chrono::duration<rep, period> duration; typedef std::chrono::time_point<clock> time_point; static const bool is_steady = true; static time_point now() noexcept { unsigned lo, hi; asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); return time_point(duration(static_cast<rep>(hi) << 32 | lo)); } private: static unsigned get_clock_speed() { int mib[] = {CTL_HW, HW_CPU_FREQ}; const std::size_t namelen = sizeof(mib)/sizeof(mib[0]); unsigned freq; size_t freq_len = sizeof(freq); if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0) return 0; return freq; } static bool check_invariants() { static_assert(1 == period::num, "period must be 1/freq"); assert(get_clock_speed() == period::den); static_assert(std::is_same<rep, duration::rep>::value, "rep and duration::rep must be the same type"); static_assert(std::is_same<period, duration::period>::value, "period and duration::period must be the same type"); static_assert(std::is_same<duration, time_point::duration>::value, "duration and time_point::duration must be the same type"); return true; } static const bool invariants; }; const bool clock::invariants = clock::check_invariants();
所以它不是便携式的。 但是如果你想在你自己的intel硬件上尝试一个高分辨率的时钟,那么它不会比这更好。 尽pipe预先警告,但是今天的时钟速度可以dynamic地改变(它们实际上不是一个编译时常量)。 而使用多处理器的机器,你甚至可以从不同的处理器获得时间戳。 但是,我的硬件上的实验还是相当不错的。 如果你坚持毫秒分辨率,这可能是一个解决方法。
这个时钟的CPU时钟速度(正如你所报告的)。 即对我来说,这个时钟每秒钟1 / 28,000,000次嘀嗒一次。 如果你想,你可以将它转换成纳秒(例如):
using std::chrono::nanoseconds; using std::chrono::duration_cast; auto t0 = clock::now(); auto t1 = clock::now(); nanoseconds ns = duration_cast<nanoseconds>(t1-t0);
转换将截断cpu周期的分数以形成纳秒。 其他舍入模式是可能的,但这是一个不同的话题。
对我来说,这将返回一个低至18个时钟滴答的持续时间,截断到6纳秒。
我在上面的时钟中增加了一些“不变的检查”,其中最重要的是检查clock::period
对于机器是否正确。 再次,这不是可移植的代码,但如果你使用这个时钟,你已经承诺。 此处显示的私有get_clock_speed()
函数在OS X上获取最大CPU频率,并且该值应与clock::period
的常量分母相同。
当你把这段代码移植到你的新机器上时,添加这个function可以节省一些debugging时间,并忘记将clock::period
更新为新机器的速度。 所有的检查都是在编译时或程序启动时完成的。 所以它不会影响clock::now()
的性能。
马修·威尔逊的STLSoft库提供了几种定时器types,具有一致的接口,所以你可以即插即用。 其中包括低成本,低分辨率的计时器,以及高分辨率但成本高的计时器。 也有用于测量线程前时间和测量每个进程时间以及测量已用时间的所有时间。
在Dobb博士的几年前有一篇详尽的文章 ,虽然它只涵盖了WinSTL子项目中定义的Windows文章。 STLSoft还在UNIXSTL子项目中提供了UNIX定时器,您可以使用“PlatformSTL”,其中包括UNIX或Windows,如下所示:
#include <platformstl/performance/performance_counter.hpp> #include <iostream> int main() { platformstl::performance_counter c; c.start(); for(int i = 0; i < 1000000000; ++i); c.stop(); std::cout << "time (s): " << c.get_seconds() << std::endl; std::cout << "time (ms): " << c.get_milliseconds() << std::endl; std::cout << "time (us): " << c.get_microseconds() << std::endl; }
HTH
StlSoft开放源代码库在Windows和Linux平台上提供了一个很好的定时器 。 如果你想自己实施,只需看看他们的来源。
ACE库也有便携式高分辨率定时器。
Doxygen高分辨率定时器:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html
这不是最好的答案,但是下面是关于高分辨率定时器的游戏开发网站的一些对话:
我强烈build议boost :: posix_time库。 它支持不同分辨率的计时器,我相信只要几微秒
C ++库问题的第一个答案通常是BOOST: http : //www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm 。 这是做你想要的吗? 可能不会,但这是一个开始。
问题是你想要便携式和计时器function是不通用的操作系统。
我已经看到这个实现了几次封闭源代码的内部解决scheme……所有这些都使用围绕本地Windows高分辨率定时器的#ifdef
解决scheme和使用struct timeval
Linux内核定时器(请参见man timeradd
)另一方面。
你可以抽象出这个和一些开源项目已经完成了 – 我看的最后一个是CoinOR类CoinTimer,但肯定有更多的。
STLSoft有一个性能库 ,它包含一组计时器类,一些适用于UNIX和Windows。
SDL2具有出色的跨平台高分辨率定时器。 但是,如果您需要亚毫秒的准确度,我在这里写了一个非常小的跨平台计时器库。 它与C ++ 03和C ++ 11 /更高版本的C ++兼容。
我不确定你的要求,如果你想计算时间间隔,请参阅下面的线程
在C程序中计算经过的时间(以毫秒为单位)
如果在项目中使用Qt框架,最好的解决scheme可能是使用QElapsedTimer。
这里晚了,但是我正在使用一个不能升级到c ++ 11的遗留代码库。 我们团队中没有人对c ++非常熟练,所以添加像STL这样的库很困难(除了其他人提出的关于部署问题的潜在顾虑之外)。 我真的需要一个非常简单的跨平台计时器,可以自己生活,没有任何东西,除了裸骨标准系统库。 这是我发现的:
http://www.songho.ca/misc/timer/timer.html
在这里重新发布整个源代码,以便在网站死亡时不会丢失:
////////////////////////////////////////////////////////////////////////////// // Timer.cpp // ========= // High Resolution Timer. // This timer is able to measure the elapsed time with 1 micro-second accuracy // in both Windows, Linux and Unix system // // AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html // CREATED: 2003-01-13 // UPDATED: 2017-03-30 // // Copyright (c) 2003 Song Ho Ahn ////////////////////////////////////////////////////////////////////////////// #include "Timer.h" #include <stdlib.h> /////////////////////////////////////////////////////////////////////////////// // constructor /////////////////////////////////////////////////////////////////////////////// Timer::Timer() { #if defined(WIN32) || defined(_WIN32) QueryPerformanceFrequency(&frequency); startCount.QuadPart = 0; endCount.QuadPart = 0; #else startCount.tv_sec = startCount.tv_usec = 0; endCount.tv_sec = endCount.tv_usec = 0; #endif stopped = 0; startTimeInMicroSec = 0; endTimeInMicroSec = 0; } /////////////////////////////////////////////////////////////////////////////// // distructor /////////////////////////////////////////////////////////////////////////////// Timer::~Timer() { } /////////////////////////////////////////////////////////////////////////////// // start timer. // startCount will be set at this point. /////////////////////////////////////////////////////////////////////////////// void Timer::start() { stopped = 0; // reset stop flag #if defined(WIN32) || defined(_WIN32) QueryPerformanceCounter(&startCount); #else gettimeofday(&startCount, NULL); #endif } /////////////////////////////////////////////////////////////////////////////// // stop the timer. // endCount will be set at this point. /////////////////////////////////////////////////////////////////////////////// void Timer::stop() { stopped = 1; // set timer stopped flag #if defined(WIN32) || defined(_WIN32) QueryPerformanceCounter(&endCount); #else gettimeofday(&endCount, NULL); #endif } /////////////////////////////////////////////////////////////////////////////// // compute elapsed time in micro-second resolution. // other getElapsedTime will call this first, then convert to correspond resolution. /////////////////////////////////////////////////////////////////////////////// double Timer::getElapsedTimeInMicroSec() { #if defined(WIN32) || defined(_WIN32) if(!stopped) QueryPerformanceCounter(&endCount); startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart); endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart); #else if(!stopped) gettimeofday(&endCount, NULL); startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec; endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec; #endif return endTimeInMicroSec - startTimeInMicroSec; } /////////////////////////////////////////////////////////////////////////////// // divide elapsedTimeInMicroSec by 1000 /////////////////////////////////////////////////////////////////////////////// double Timer::getElapsedTimeInMilliSec() { return this->getElapsedTimeInMicroSec() * 0.001; } /////////////////////////////////////////////////////////////////////////////// // divide elapsedTimeInMicroSec by 1000000 /////////////////////////////////////////////////////////////////////////////// double Timer::getElapsedTimeInSec() { return this->getElapsedTimeInMicroSec() * 0.000001; } /////////////////////////////////////////////////////////////////////////////// // same as getElapsedTimeInSec() /////////////////////////////////////////////////////////////////////////////// double Timer::getElapsedTime() { return this->getElapsedTimeInSec(); }
和头文件:
////////////////////////////////////////////////////////////////////////////// // Timer.h // ======= // High Resolution Timer. // This timer is able to measure the elapsed time with 1 micro-second accuracy // in both Windows, Linux and Unix system // // AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html // CREATED: 2003-01-13 // UPDATED: 2017-03-30 // // Copyright (c) 2003 Song Ho Ahn ////////////////////////////////////////////////////////////////////////////// #ifndef TIMER_H_DEF #define TIMER_H_DEF #if defined(WIN32) || defined(_WIN32) // Windows system specific #include <windows.h> #else // Unix based system specific #include <sys/time.h> #endif class Timer { public: Timer(); // default constructor ~Timer(); // default destructor void start(); // start timer void stop(); // stop the timer double getElapsedTime(); // get elapsed time in second double getElapsedTimeInSec(); // get elapsed time in second (same as getElapsedTime) double getElapsedTimeInMilliSec(); // get elapsed time in milli-second double getElapsedTimeInMicroSec(); // get elapsed time in micro-second protected: private: double startTimeInMicroSec; // starting time in micro-second double endTimeInMicroSec; // ending time in micro-second int stopped; // stop flag #if defined(WIN32) || defined(_WIN32) LARGE_INTEGER frequency; // ticks per second LARGE_INTEGER startCount; // LARGE_INTEGER endCount; // #else timeval startCount; // timeval endCount; // #endif }; #endif // TIMER_H_DEF