cpp11计时器-chrono

计时有很多用途,比如:测试某一段代码的运行时间,时间越短,则性能相对越高。计时有很多方法,我将首先给出我以前常用的一种计时方法,然后给出我认为的最佳方法(采用了C++11的标准技术)。

首先给出我以前常用的一种“传统”的计时方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <ctime>
#include <iostream>
using namespace std;

int main()
{

clock_t start = clock();
// do something...
clock_t end = clock();
cout << "花费了" << (double)(end - start) / CLOCKS_PER_SEC << "秒" << endl;
}

此方法可以精确到毫秒,输出样例:花费了0.123秒

c++11计时器

C++11有了chrono库,可以在不同系统中很容易的实现定时功能。

要使用chrono库,需要#include,其所有实现均在std::chrono namespace下。注意标准库里面的每个命名空间代表了一个独立的概念。

chrono是一个模版库,使用简单,功能强大,只需要理解三个概念:duration、time_point、clock

时钟

chrono库定义了三种不同的时钟:

std::chrono::system_clock: 依据系统的当前时间 (不稳定)
std::chrono::steady_clock: 以统一的速率运行(不能被调整)
std::chrono::high_resolution_clock: 提供最高精度的计时周期(可能是steady_clock或者system_clock的typedef)

system_clock就类似Windows系统右下角那个时钟,是系统时间。明显那个时钟是可以乱设置的。明明是早上10点,却可以设置成下午3点。

steady_clock则针对system_clock可以随意设置这个缺陷而提出来的,他表示时钟是不能设置的。

high_resolution_clock则是一个高分辨率时钟。

这三个时钟类都提供了一个静态成员函数now()用于获取当前时间,该函数的返回值是一个time_point类型,

时间精度其实也就是时间分辨率。抛开时间量纲单论分辨率,其实就是一个比率。如:1000110001、101101、1111 、110110、1100011000。这些比率加上距离量纲就变成距离分辨率,加上时间量纲就变成时间分辨率了。为此,C++11定义了一个新的模板类ratio,用于表示比率,定义如下:

1
2
3
//#include<ration>
template<std::intmax_t Num, std::intmax_t Denom = 1> //前者是分子,后者是分母
class ratio;

为了方便,C++标准委员会还预定义了下面这些分辨率,供用户使用。

1
2
3
4
5
6
7
//#include<ratio>
typedef ratio<1, 1000000000000000000> atto;
typedef ratio<1, 1000000000000000> femto;
typedef ratio<1, 1000000000000> pico;
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <chrono>   
#include <iostream>
using namespace std;
using namespace chrono;

int main()
{
auto start = system_clock::now();
// do something...
auto end = system_clock::now();
auto duration = duration_cast<microseconds>(end - start);
cout << "花费了" << double(duration.count()) * microseconds::period::num / microseconds::period::den << "秒" << endl;
}

此方法可以精确到微妙,输出样例:花费了0.123456秒

microseconds 表示微妙。除此之外,还有五种时间单位:hours, minutes, seconds, milliseconds, nanoseconds
num 和 den分别表示分子(numerator)和分母(denominator)。在我给出的代码中,num等于1, den等于1,000,000

system_clock除了now()函数外,还提供了to_time_t()静态成员函数。用于将系统时间转换成熟悉的std::time_t类型,

得到了time_t类型的值,在使用ctime()函数将时间转换成字符串格式,就可以很方便地打印当前时间了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
#include<vector>
#include<string>
#include<ctime>//将时间格式的数据转换成字符串
#include<chrono>
using namespace std::chrono;
using namespace std;
int main()
{
//获取系统的当前时间
auto t = system_clock::now();
//将获取的时间转换成time_t类型
auto tNow = system_clock::to_time_t(t);

//ctime()函数将time_t类型的时间转化成字符串格式,这个字符串自带换行符
string str_time = std::ctime(&tNow);

cout<<str_time;

return 0;
}

二、持续的时间 - duration

std::chrono::duration<int,ratio<60,1>> ,表示持续的一段时间,这段时间的单位是由ratio<60,1>决定的,int表示这段时间的值的类型,函数返回的类型还是一个时间段duration

std::chrono::duration<double,ratio<60,1>>

由于各种时间段(duration)表示不同,chrono库提供了duration_cast类型转换函数。

duration_cast用于将duration进行转换成另一个类型的duration。

duration还有一个成员函数count(),用来表示这一段时间的长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
#include<string.h>
#include<chrono>
using namespace std::chrono;
using namespace std;
int main()
{
auto start = steady_clock::now();
for(int i=0;i<100;i++)
cout<<"nice"<<endl;
auto end = steady_clock::now();

auto tt = duration_cast<microseconds>(end - start);

cout<<"程序用时="<<tt.count()<<"微秒"<<endl;
return 0;
}

三、时间点 - time_point

1
2
3
//#include<chrono>
template< class Clock, class Duration = typename Clock::duration >
class time_point;

std::chrono::time_point 表示一个具体时间,如上个世纪80年代、今天下午3点、火车出发时间等,只要它能用计算机时钟表示。
第一个模板参数Clock用来指定所要使用的时钟(标准库中有三种时钟,system_clock,steady_clock和high_resolution_clock。见4时钟详解),第二个模板函数参数用来表示时间的计量单位(特化的std::chrono::duration<> )

时间点都有一个时间戳,即时间原点。chrono库中采用的是Unix的时间戳1970年1月1日 00:00。所以time_point也就是距离时间戳(epoch)的时间长度(duration)。

时间点有个重要的函数:duration time_since_epoch() (用于获取当前时间点距离时间戳的时间长度)
即经常用来得到当前时间点到1970年1月1日00:00的时间距离、该函数返回的duration的精度和构造time_point的时钟(Clock)有关(见4时钟详解)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <chrono>
#include <ctime>
using namespace std;
int main()
{
//距离时间戳2两秒
chrono::time_point<chrono::system_clock, chrono::seconds> tp(chrono::seconds(2));
cout << "to epoch : " <<tp.time_since_epoch().count() << "s" <<endl;
//转化为ctime,打印输出时间点
time_t tt = chrono::system_clock::to_time_t(tp);
char a[50];
ctime_s(a, sizeof(a), &tt);
cout << a;
system("pause");
return 0;
}