我希望在C ++中实现一个简单的计时器机制。该代码应该适用于Windows和Linux。分辨率应尽可能精确(至少精确到毫秒)。这将用于简单地跟踪时间的流逝,而不是实现任何类型的事件驱动设计。实现这一目标的最佳工具是什么?
答案 0 :(得分:138)
更新旧问题的答案:
在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问题的扩展程序,可以免费使用here。
还有一个<chrono>
的实现可用于boost(可能仍然在主干上,不确定它是否已被释放)。
<强>更新强>
这是对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();
所以它不便携。但是如果你想在自己的英特尔硬件上试验高分辨率时钟,它就不会比这更好。虽然预先警告,但今天的时钟速度可以动态变化(它们实际上不是编译时常量)。使用多处理器机器,您甚至可以从不同的处理器获取时间戳。但是,我的硬件实验仍然相当不错。如果你坚持毫秒级分辨率,这可能是一种解决方法。
此时钟具有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()
的性能。
答案 1 :(得分:38)
对于C ++ 03 :
Boost.Timer可能会有效,但它取决于C函数clock
,因此可能没有足够好的分辨率。
Boost.Date_Time包含之前在Stack Overflow上推荐的ptime
class。请参阅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的this answer。
答案 2 :(得分:6)
Matthew Wilson的STLSoft libraries提供了几种计时器类型,具有全等接口,因此您可以即插即用。其中包括低成本但低分辨率的定时器,以及高分辨率但成本高的定时器。还有一些用于测量预螺纹时间和测量每个工艺时间,以及测量经过时间的所有时间。
几年前有一个详尽无遗的article covering it in Dr. Dobb's,虽然它只涵盖了Windows,即WinSTL子项目中定义的那些。 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
答案 3 :(得分:5)
StlSoft开源库在Windows和Linux平台上提供了一个非常good timer。如果你想让它自己实现,只需看看它们的来源。
答案 4 :(得分:5)
ACE库也有便携式高分辨率计时器。
高分辨率计时器的氧气:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html
答案 5 :(得分:5)
这不是最好的答案,但这里有关于高分辨率计时器的游戏开发网站的一些对话:
答案 6 :(得分:4)
我强烈推荐使用boost :: posix_time库。它支持各种分辨率的定时器,相当于微秒,我相信
答案 7 :(得分:3)
我已经看到这个实现了几次作为闭源内部解决方案......一方面都使用本地Windows高分辨率计时器的#ifdef
解决方案和使用{Linux内核计时器{1}}(另请参阅struct timeval
)。
你可以抽象一下,一些开源项目已经完成了 - 我看到的最后一个是CoinOR class CoinTimer,但肯定有更多。
答案 8 :(得分:3)
答案 9 :(得分:2)
C ++库问题的第一个答案通常是BOOST:http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm。这样做你想要的吗?可能不是,但这是一个开始。
问题是你想要便携式和定时器功能在操作系统中不通用。
答案 10 :(得分:2)
STLSoft有一个Performance Library,其中包含一组计时器类,其中一些适用于UNIX和Windows。
答案 11 :(得分:1)
我不确定您的要求,如果您想计算时间间隔,请参阅下面的帖子
答案 12 :(得分:0)
如果在项目中使用Qt框架,最好的解决方案可能是使用QElapsedTimer。
答案 13 :(得分:0)
这里的聚会晚了,但我还在一个无法升级到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
答案 14 :(得分:0)
我发现这看起来很有希望,而且非常简单,不确定是否有任何缺点:
https://gist.github.com/ForeverZer0/0a4f80fc02b96e19380ebb7a3debbee5
/* ----------------------------------------------------------------------- */
/*
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
}
/* ----------------------------------------------------------------------- */-------------------------------- */
答案 15 :(得分:0)
Here 是 C++11 中的一个实现。有关详细信息,请参阅 co/time.h、src/time.cc、src/time_win.cc。使用起来很简单,如下:
#include "co/defer.h"
#include "co/time.h"
#include "co/log.h"
{
Timer t;
defer(LOG << "time elapse: " << t.us() << " us");
// your code block here..
}