计时是发掘系统中时间性能瓶颈的必不可少的一环。
由于C++语言存在各种不同的实现版本,可选用的计时方案也比较多,本文介绍了各种常用的跨平台计时方案,包括:
- C计时方案
clock
- C计时方案
time
- STL计时方案
std::chrono::clocks
- QT计时方案
QTime
- Boost计时方案
timer
而对于平台依赖的方案,不在本文的介绍范围之内,比如:Windows平台下的GetTickCount
和QueryPerformanceCounter
,*inux平台下的hrtimer
和gettimeofday
。
本文以task()
函数为例来说明各计时方案用法,在之后的代码中,就不再定义了:
void task()
{
static int value = 0;
while(0 <= value++);
}
C计时方案clock
clock
是C标准库中的函数,在time.h
中声明,函数签名如下:
#include
clock_t clock(void);
值得说明一下的是,clock
返回的是处理器调用某个进程或函数所花费的时间,而不是系统时钟时间,也就是说,如果调用过程有sleep
,不会计算在内。
此外,函数返回结果clock_t
类型值需要除以CLOCKS_PER_SEC
才能得到秒数值。而且对于不同平台,CLOCKS_PER_SEC
的值也是不一样的,笔者知道的取值有1000
和1000000
。
下面代码说明用法:
#include
#include
void task();
int main()
{
std::clock_t start = std::clock();
task();
std::clock_t end = std::clock();
double duration = (end - start)/(double)CLOCKS_PER_SEC;
std::cout << duration << std::endl;
}
C计时方案time
time
与clock
一样,也是在time.h
中声明的C标准库函数,函数签名如下:
#include
time_t time (time_t *__timer);
与clock
不同的是,time
传入一个time_t*
,然后返回系统时钟时间,而不是CPU时间。
其基本用法如下:
#include
#include
void task();
int main()
{
std::time_t start, end;
std::time(&start);
task();
std::time(&end);
double duration = std::difftime(end, start);
std::cout << duration << std::endl;
}
STL计时方案std::chrono::clocks
C++ 11提供了高精度时间的标准库chrono,其中抽象了三个概念:
- 时间点:
std::chrono::steady_clock::time_point
- 时钟:
std::chrono::clocks
- 时间间隔:
std::chrono::duration
std::chrono::clocks
用来计时,实现的时钟有三个:
-
system_clock
:系统时钟,与系统显示时间保持一致,可人为修改。 -
steady_clock
:稳定时钟,不随系统时间变化,不可修改。 -
high_resolution_clock
:高精度时间,目前存在移植性问题,暂不推荐使用。
由此可见,采用steady_clock
最合适。
下面来看基本用法:
#include
#include
void task();
int main()
{
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
task();
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::milliseconds duration =
std::chrono::duration_cast(end - start);
std::cout << duration.count() / 1000.0 << std::endl;
}
其中(end - start)
需要利用duration_cast
转换成需要的统计单位。
QT计时方案QTime
QT计时方案主要采用QTime
这个类,用到函数:
-
QTime::start()
方法用于启动计时器 -
QTime::restart()
方法用于重启计时器 -
QTime::elapsed()
方法返回计时器从启动(start
或restart
)到当前的毫秒数。
用法简单举例如下:
#include
#include
void task();
int main()
{
QTime time;
time.start();
task();
int duration = time.elapsed();
std::cout << duration / 1000.0 << std::endl;
}
Boost计时方案timer
Boost是业界比较流行的C++库,其中用到的计时类boost::timer
与QT非常相似:
- 对象构造完成时,定时器启动
-
boost::timer::restart()
重新启动定时器 -
boost::timer::elapsed()
返回启动的时间,单位为秒
下面说明用法:
#include
#include
void task();
int main()
{
boost::timer t;
task();
double duration = t.elapsed();
std::cout << duration << std::endl;
}
总结
本文介绍了常用的跨平台计时方案,从功能角度考虑,clock
返回CPU运行时间,而其余均返回时钟时间;从精度角度来看,std::chrono::clocks
精度最高,达到纳秒级别;从接口的角度,QTime
和boost::timer
提供了专门的timer类,接口更加优雅,其余方案仅仅是对时钟的一种使用。
综合考虑,笔者会推荐std::chrono::clocks
。
名称 | 功能 | 精度 | 接口设计 | 通用性 |
---|---|---|---|---|
clock |
CPU时间 | 毫秒/微秒 | 一般 | 好 |
time |
时钟时间 | 毫秒/微秒 | 一般 | 一般 |
std::chrono::clocks |
时钟时间 | 纳秒 | 一般 | 好 |
QTime |
时钟时间 | 毫秒 | 好 | 一般 |
boost::timer |
时钟时间 | 毫秒/微秒 | 好 | 一般 |
说在最后
计时 一般指代码块从开始到结束,系统运行的时间,也可能指CPU时间片的时间,是一个不太精确的数值。如果需要精确考虑某个线程运行时间或某个过程的系统时间和用户时间,则需要进一步考虑其他解决方案。