0%

C++11新特性 - 时钟与计时器

C++11 引入了新的时间标准库 chrono, 本文将简述其使用

在传统的 C++ 中,如果我们希望知道一段代码运行了多少时间,可以这样做

1
2
3
4
5
6
7
8
9
10
int main() {
unsigned long start = clock();

for (int i = 0; i < 1000000000; i++);

unsigned long end = clock();

// 1.64376
cout << (double)(end - start) / CLOCKS_PER_SEC << endl;
}

C++11 则通过 chrono 库提供了更加灵活的时间操作,要想在 C++11 中完成同样的操作,首先需要引入 3 个概念, clock、duration 和 time_point

clock

clock 表示计时的时钟,C++11 提供了 3 种 clock

  • steady_clock,稳定时钟,保证每时每刻以稳定速率增长
  • system_clock,系统时钟,可能不稳定,因为人或其它程序可以修改
  • high_resolution_clock,高精度钟,拥有最小系统可提供的时间单位(在我本机 clang11 的标准库中,high_resolution_clock 就是 steady_clock)

duration

duration 表示时间间隔,我们平时经常说做某件事“用了2个小时”,这里“2个小时”就是一个时间间隔,它由两部分组成,数量 2 和单位小时,C++11 中,duration 也拥有类似的定义

1
2
3
4
template<
class Rep,
class Period = std::ratio<1>
> class duration;

其中,Rep 存储时间间隔的基础类型,C++11 通常使用 long, long long 这些整型表示; Period 则用一个有理数表示了该时间间隔和秒之间的换算关系

例如,小时的定义是

1
typedef duration<long, ratio<3600> > hours;

意味着,小时的数量内部用长整型表示,且一小时等于 3600 秒

C++11 预定义了常用的时间间隔类型,包括 hours, minutes, seconds, milliseconds, microseconds, nanoseconds

duration 之间是可以互相换算且可加减的

1
2
3
4
5
microseconds mi(2745);
nanoseconds na = mi; // 2745000, 向高精度隐式类型转换
milliseconds mill = duration_cast<milliseconds>(mi); // 2, 向低精度必须显式类型转换,且会被取整

cout << (mill + mi).count() << endl; // 4745,不同精度也可相加,转换为较高精度

time_point

在现实中,要表示一个时间点,例如“上海交通大学建校的时间是 1896 年 4 月 8 日”,这里“1896 年 4 月 8 日”代表的含义是,这一天距离耶稣诞生已经过去了 1896 年 4 个月零 8 天了。在这里,“耶稣诞生”是一个众所周知的事件发生的时间,而“1896 年 4 个月零 8 天”代表两个事件之间的时间间隔

C++11中,time_point 也拥有类似的定义

1
2
3
4
template<
class Clock,
class Duration = typename Clock::duration
> class time_point;

这意味着 time_point 是以 Duration 作为单位,衡量现在距离 Clock 类的起始参考时间点经过了多少时间间隔

例如 system_clock::time_point,以 microseconds 作为单位,衡量距离 system_clock 参考时间点(Unix 纪元起始,即 1970-01-01) 的时间间隔,steady_clock 则与具体实现有关

基于这个特性,我们可以很简单地获取当前的 unix timestamp

1
2
3
4
system_clock::time_point now = system_clock::now();

// 1583849550830907 (微秒), 2020-03-10
cout << now.time_since_epoch().count() << endl;

回到最初的话题,如何基于 chrono 测量一段代码的运行时长呢?

1
2
3
4
5
6
7
8
9
auto start = steady_clock::now();
for (int i = 0; i < 1000000000; i++);
auto end = steady_clock::now();

// steady_clock::duration 和秒的换算关系
double ratio = (double) steady_clock::duration::period::num / steady_clock::duration::period::den;

// 输出 1.72885
cout << duration_cast<microseconds>(end - start).count() * ratio;