我希望在C++中实现一个简单的计时器机制。代码应该能在Windows和Linux上运行。计时器的精度应该尽可能高(至少精确到毫秒级别)。这将用于简单跟踪时间的流逝,而不是实现任何形式的事件驱动设计。最佳工具是什么?
我希望在C++中实现一个简单的计时器机制。代码应该能在Windows和Linux上运行。计时器的精度应该尽可能高(至少精确到毫秒级别)。这将用于简单跟踪时间的流逝,而不是实现任何形式的事件驱动设计。最佳工具是什么?
针对一个旧问题的更新回答:
在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"是一个扩展程序,旨在简化使用这些新类型的I/O问题,并可在此处免费获取。
boost库中也有一个实现了<chrono>
的版本(可能仍在开发中,不确定是否已发布)。
更新
这是对Ben下面的评论做出的回应,他指出在VS11中后续对std::chrono::high_resolution_clock
的调用需要几毫秒的时间。以下是一个<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();
所以它不是可移植的。但如果你想在自己的英特尔硬件上尝试使用高分辨率时钟,那么它就再好不过了。不过要注意,今天的时钟速度可以动态更改(它们并不是编译时常量)。而且在多处理器机器上,甚至可以从不同的处理器获取时间戳。但是,在我的硬件上进行实验效果还是不错的。如果你只能获得毫秒级分辨率,那么这可能是一个解决方法。
该时钟的持续时间以您 CPU 的时钟速度(如您所报告的)为单位。也就是说,对于我来说,此时钟每 1/2,800,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
的常量分母相同。
添加此代码将在你将此代码移植到新机器并忘记将 clock::period
更新为新机器速度时,为你节省一些调试时间。所有检查都在编译时或程序启动时完成。因此,它不会影响 clock::now()
的性能。
high_resolution_clock
的最短非零间隔是几毫秒,这是不幸的。 - Petter对于C++03:
Boost.Timer可能适用,但它依赖于C函数clock
,因此可能无法具有足够的分辨率。
Boost.Date_Time包括ptime
类,在Stack Overflow上曾经推荐过。请查看其关于microsec_clock::local_time
和microsec_clock::universal_time
的文档,但请注意其警告:"Win32系统通常无法通过此API实现微秒分辨率。"
STLsoft提供了许多内容,其中包括围绕特定操作系统API的跨平台(Windows和Linux / Unix)C ++封装。其performance library有几个类可以满足您的需求。(为了使其跨平台,请选择像performance_counter
这样的类,该类存在于winstl
和unixstl
名称空间中,然后使用与您的平台匹配的名称空间。)
对于C++11及以上版本:
std::chrono
库内置了这个功能。请参见@HowardHinnant的这个答案以了解详情。
<chrono>
和 <thread>
)以标准且可移植的方式来实现这一点吗?如果可能的话,怎么做? - Manu343726Matthew Wilson的STLSoft库提供了几种计时器类型,具有一致的接口,因此您可以即插即用。这些提供中既有低成本但低分辨率的计时器,也有高分辨率但高成本的计时器。还有用于测量线程前时间和测量进程时间的计时器,以及所有测量经过时间的计时器。
多年前,Dr. Dobb's上的一篇详尽文章介绍了它,虽然只涵盖了Windows的计时器,这些计时器定义在WinSTL子项目中。STLSoft还为UNIX计时器提供了UNIXSTL子项目,并且您可以使用"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
ACE库还具有可移植的高分辨率计时器。
高分辨率计时器的Doxygen文档:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html
#ifdef
解决方案,另一方面则使用 Linux 内核计时器,并使用 struct timeval
(参见 man timeradd
)。我强烈推荐使用 boost::posix_time 库。它支持定时器的多种分辨率,甚至可以达到微秒级别。
/* ----------------------------------------------------------------------- */
/*
Easy embeddable cross-platform high resolution timer function. For each
platform we select the high resolution timer. You can call the 'ns()'
function in your file after embedding this.
*/
#include <stdint.h>
#if defined(__linux)
# define HAVE_POSIX_TIMER
# include <time.h>
# ifdef CLOCK_MONOTONIC
# define CLOCKID CLOCK_MONOTONIC
# else
# define CLOCKID CLOCK_REALTIME
# endif
#elif defined(__APPLE__)
# define HAVE_MACH_TIMER
# include <mach/mach_time.h>
#elif defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
static uint64_t ns() {
static uint64_t is_init = 0;
#if defined(__APPLE__)
static mach_timebase_info_data_t info;
if (0 == is_init) {
mach_timebase_info(&info);
is_init = 1;
}
uint64_t now;
now = mach_absolute_time();
now *= info.numer;
now /= info.denom;
return now;
#elif defined(__linux)
static struct timespec linux_rate;
if (0 == is_init) {
clock_getres(CLOCKID, &linux_rate);
is_init = 1;
}
uint64_t now;
struct timespec spec;
clock_gettime(CLOCKID, &spec);
now = spec.tv_sec * 1.0e9 + spec.tv_nsec;
return now;
#elif defined(_WIN32)
static LARGE_INTEGER win_frequency;
if (0 == is_init) {
QueryPerformanceFrequency(&win_frequency);
is_init = 1;
}
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart);
#endif
}
/* ----------------------------------------------------------------------- */-------------------------------- */
对于C++库相关的问题,通常的答案是使用BOOST:http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm。这个库是否满足你的需求呢?可能不太符合,但至少可以作为一个起点。
问题在于你需要一个可移植的计时器函数,但这种函数在各种操作系统中并不普遍存在。
(至少毫秒级的精度)
并不是很高的分辨率。 - undefined