前言
计时器可能是大多数程序员都自己实现过的一个功能,尤其是C++
这种没有计时器类可以直接调用的,一般都会自己实现一个,而由于硬件的原因,想要一个跨平台的计时器功能还要兼顾不同的硬件平台,增加程序员的工作量。C++11
标准发布之后,新增了chrono
命名空间,里面包含了大量与时间相关的功能,利用它可以非常方便的写出一个计时器类。
旧式计时器类
先说一下以往的方法,在 Windows
平台上,想要实现一个高精度的计时器,一般会用到2个 Windows API
, QueryPerformanceFrequency
和 QueryPerformanceCounter
,配合使用获取高精度时间间隔。而 linux
平台则一般使用 gettimeofday
函数,可以获取微妙精度的时间,如需更高精度,则可能要用到汇编。
接口
它可能长这样,为了兼顾不同平台,需要预处理宏的帮忙。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| #include <time.h>
#ifdef _WIN32
#include <windows.h>
#if !defined(_WINSOCK2API_) && !defined(_WINSOCKAPI_)
struct timeval {
long tv_sec;
long tv_usec;
};
#endif
#else//_WIN32
#include <sys/time.h>
#endif//_WIN32
typedef double timer_dt;
class Timer {
public:
Timer();
~Timer() {};
void start();
void stop();
timer_dt get_time();
#ifdef _WIN32
double freq;
LARGE_INTEGER start_time;
LARGE_INTEGER finish_time;
#else//_WIN32
struct timeval start_time;
struct timeval finish_time;
#endif//_WIN32
};
|
实现
核心的3个函数start()
、stop()
和get_time()
的实现可能是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| // 构造函数里获取高精度时钟的频率
Timer::Timer() {
#ifdef _WIN32
LARGE_INTEGER tmp;
QueryPerformanceFrequency((LARGE_INTEGER*)&tmp);
freq = (double)tmp.QuadPart/1000.0;
#endif
}
void Timer::start() {
#ifdef _WIN32
QueryPerformanceCounter((LARGE_INTEGER*) &start_time);
#else//_WIN32
gettimeofday(&start_time, 0);
#endif//_WIN32
}
void Timer::stop() {
#ifdef _WIN32
QueryPerformanceCounter((LARGE_INTEGER*) &finish_time);
#else//_WIN32
gettimeofday(&finish_time, 0);
#endif//_WIN32
}
timer_dt Timer::get_time() {
timer_dt interval = 0.0f;
#ifdef _WIN32
interval = (timer_dt)((double)(finish_time.QuadPart
- start_time.QuadPart) / freq);
#else
// time difference in milli-seconds
interval = (timer_dt) (1000.0 * ( finish_time.tv_sec - start_time.tv_sec)
+(0.001 * (finish_time.tv_usec - start_time.tv_usec)));
#endif//_WIN32
return interval;
}
|
C++11 计时器类
chrono
C++11
标准发布之后,给我们带来了 chrono
命名空间,里面的东西虽说名字特别难记:P,但是用起来很方便,用它来实现一个跨平台、高精度的计时器类只需要十几行代码,甚至核心代码只有3行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include <chrono>
// 这里为了方便说明直接用了该命名空间,实际情况请酌情使用
using namespace std::chrono;
class Timer {
public:
Timer() : tpStart(high_resolution_clock::now()), tpStop(tpStart) {}
public:
void start() { tpStart = high_resolution_clock::now(); }
void stop() { tpStop = high_resolution_clock::now(); }
template <typename span>
auto delta() const { return duration_cast<span>(high_resolution_clock::now() - tpStart).count(); }
private:
time_point<high_resolution_clock> tpStart;
time_point<high_resolution_clock> tpStop;
};
|
没错,只需要这些代码就可以实现一个跨平台且高精度的计时器类,使用方法也非常简单,可以按需选择精度,例:
1
2
3
4
5
6
7
| Timer t;
// some code here
t.stop();
// 调用 delta() 函数时需指定精度,以下四种均在chrono命名空间内定义
// seconds, milliseconds, microseconds, nanoseconds
std::cout << "the xxx time is: " << t.delta<nanoseconds>() << std::endl; // 输出纳秒
std::cout << "the xxx time is: " << t.delta<milliseconds>() << std::endl; // 输出毫秒
|
为了方便使用,还可以加入以下两个函数。
1
2
3
4
5
6
7
8
9
10
| template <typename span>
auto stop_delta() { stop(); return duration_cast<span>(tpStop - tpStart).count(); }
template <typename span>
auto stop_delta_start() {
stop();
auto ts = duration_cast<span>(tpStop - tpStart).count();
start();
return ts;
}
|
ratio
C++11还增加了 ratio
命名空间,顾名思义,这个命名空间里定义了一堆比率,如 std::milli
、 std::micro
、 std::nano
,配合这些比率我们就可以实现一个返回浮点数的计时器了,核心代码如下,只是修改了一点点内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| #include <chrono>
#include <ratio>
// 这里为了方便说明直接用了该命名空间,实际情况请酌情使用
using namespace std::chrono;
class Timer {
public:
using s = std::ratio<1, 1>;
using ms = std::ratio<1, 1000>;
using us = std::ratio<1, 1000000>;
using ns = std::ratio<1, 1000000000>;
public:
Timer() : tpStart(high_resolution_clock::now()), tpStop(tpStart) {}
public:
void start() { tpStart = high_resolution_clock::now(); }
void stop() { tpStop = high_resolution_clock::now(); }
template <typename span>
auto delta() const { return duration<double, span>(high_resolution_clock::now() - tpStart).count(); }
template <typename span>
auto stop_delta() { stop(); return duration<double, span>(tpStop - tpStart).count(); }
template <typename span>
auto stop_delta_start() {
stop();
auto ts = duration<double, span>(tpStop - tpStart).count();
start();
return ts;
}
private:
time_point<high_resolution_clock> tpStart;
time_point<high_resolution_clock> tpStop;
};
|
总结
C++11新增的这个 chrono
命名空间非常方便好用,由于是标准库,在跨平台方面具有天然优势,在代码简洁程度上也比原来要好,配合模板更加可以减少代码量,当然它不止能用来做计时器用,还有很多别的方面的应用,在此就不多说了,以后可能会补充。