使用C++11实现一个简易计时器类

前言

计时器可能是大多数程序员都自己实现过的一个功能,尤其是C++这种没有计时器类可以直接调用的,一般都会自己实现一个,而由于硬件的原因,想要一个跨平台的计时器功能还要兼顾不同的硬件平台,增加程序员的工作量。C++11标准发布之后,新增了chrono命名空间,里面包含了大量与时间相关的功能,利用它可以非常方便的写出一个计时器类。

旧式计时器类

先说一下以往的方法,在 Windows 平台上,想要实现一个高精度的计时器,一般会用到2个 Windows APIQueryPerformanceFrequencyQueryPerformanceCounter ,配合使用获取高精度时间间隔。而 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::millistd::microstd::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 命名空间非常方便好用,由于是标准库,在跨平台方面具有天然优势,在代码简洁程度上也比原来要好,配合模板更加可以减少代码量,当然它不止能用来做计时器用,还有很多别的方面的应用,在此就不多说了,以后可能会补充。

Built with Hugo
主题 StackJimmy 设计