C++精确到纳秒计时

一般来说对于算法的计时是用不到纳秒级精度的。然而,如果真的遇到微秒都无法区分的时间间隔,就不得不用纳秒来计时了。在Java中,我们使用System.nanoTime就可以得到精确到纳秒的当前时间,而在C++中就没有提供如此简便的方法。

使用C++11标准里的chrono库

如果你使用C++11标准,问题就很容易解决了。C++11开始加入了一个std::chrono库,里面的计时方法已经可以精确到纳秒级。

high_resolution_clock和system_clock

根据cppreference上的文档,high_resolution_clock类代表了该平台最高精度的时钟,然而在MinGW-w64中我都没有找到这个类,在ubuntu下搜索时发现chrono头文件中有这样的描述:

chrono
1
2
3
4
5
6
7
8
/**
* @brief Highest-resolution clock
*
* This is the clock "with the shortest tick period." Alias to
* std::system_clock until higher-than-nanosecond definitions
* become feasible.
*/
using high_resolution_clock = system_clock;

这说明至少在ubuntu下,system_clock类的精度已经能达到纳秒级。
然而在MinGW-w64上使用system_clock时发现了一个诡异的问题,输出的时间间隔最后两位都是0,说明这个时钟只精确到了纳秒的百位,这显然不是我们想要的结果。

steady_clock

除了system_clock和high_resolution_clock类以外,chrono下还有steady_clock类。根据文档,这个时钟的时间只能增加而不能减少,而且其具体时间和真实时间无关。文档里还说这个类最适合用于计时,而如果你要获取当前时间,它就显得无能为力了。从具体结果来看,MinGW-w64中使用这个类可以精确到纳秒了,在ubuntu下更没有问题。
不过,在MinGW32下,无论使用哪个类都无法精确到1纳秒,最高也只能到1微秒。
下面就是测试用的程序:

chronotest.cpp
1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<chrono>
using namespace std;
using namespace chrono;
int main(){
auto t0=system_clock::now();
for(int i=0;i<=1000;i++)cout<<"*"<<endl;
auto t1=system_clock::now();
auto d=duration_cast<nanoseconds>(t1-t0);
cout<<d.count()<<"ns"<<endl;
return 0;
}

在MinGW32下编译时注意加上编译参数-std=gnu++11,-std=c++11会出现谜之错误。MinGW-w64和ubuntu下两个参数都能通过编译。

Note:程序中的nanoseconds可以替换成其他时间单位,具体可以查看chrono头文件。

使用timespec和clock_gettime

如果你不想使用最新的C++11标准或者你使用的是C而不是C++,就用timespec和clock_gettime吧。不过,在不同平台上timespec和clock_gettime的具体使用不尽相同,而网上的资料基本上没有说明这个问题,这完全就给我这样的C++鶸挖了一个大坑。

MinGW32

MinGW32下有timespec,然而并没有提供clock_gettime方法,所以用timespec计时不太现实。

MinGW-w64或ubuntu

如果你用的是MinGW-w64或ubuntu,就包含以下库:

headers
1
#include<sys/time.h>

然后通过以下代码即可在获得精确到纳秒级的系统时间,其中tt.tv_sec为秒,tt.tv_nsec为纳秒:
nanotime
1
2
3
timespec tt;
clock_gettime(CLOCK_REALTIME,&tt);
cout<<tt.tv_sec<<endl<<tt.tv_nsec<<endl;

不过值得注意的是,MinGW-w64下用timespec和clock_gettime也并不能真正精确到纳秒的个位,最多也只能精确到0.1微秒。

降低精度使用timeval

如果你只需要精确到微秒,除了使用timespec以外,还能使用timeval,而这是MinGW32、MinGW-w64和ubuntu通用的。

millitime
1
2
3
4
5
6
7
8
9
#include<iostream>
#include<sys/time.h>
using namespace std;
int main(){
timeval t;
gettimeofday(&t,NULL);
cout<<t.tv_sec<<endl<<t.tv_usec<<endl;
return 0;
}

总结

随着CPU性能的提升,CPU能区分的时间间隔越来越短。从Pentium处理器开始,CPU就可以精确到纳秒计时了。不过,由于能用到如此高精度时间的机会并不多,所以获取纳秒级时间没那么容易。尤其对于C++这种非跨平台的语言,获得纳秒更加复杂。经过研究,在MinGW32、MinGW-w64、ubuntu中,ubuntu下以纳秒计时最容易,几乎所有方法都能用;MinGW-w64次之,不过有的方法并不能真正精确到纳秒级;而在MinGW32下,以我三脚猫的水平看来似乎没有简单粗暴的方法了。

文章目录
  1. 1. 使用C++11标准里的chrono库
    1. 1.1. high_resolution_clock和system_clock
    2. 1.2. steady_clock
  2. 2. 使用timespec和clock_gettime
    1. 2.1. MinGW32
    2. 2.2. MinGW-w64或ubuntu
  3. 3. 降低精度使用timeval
  4. 4. 总结
|