通过菜鸟测试分析chrono的精确度
1秒(s) = 100分秒(ds) = 1000 毫秒(ms) = 1e6 微秒(μs) = 1e9 纳秒(ns) = 1e12 皮秒(ps) = 1e15 飞秒(fs) = 1e18 阿秒(as) = 1e21 仄秒(zs) = 1e24 幺秒(ys)
前言 因为在项目中使用了C++11 chrono库的system_clock:
1 #define GET_TIME_STAMP std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()
来获取时间戳用以计时,看着很复杂,有时间来分析一下chrono的精确度。
铺垫 语言层面上的时间函数都或多或少存在误差,翻阅资料后决定通过计算机的高精度计数器HPET来计算运行时间。
1 2 3 4 5 6 7 BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); // 获取高精度计数器的时钟频率 BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount); // 获取从上电以来计数器计数 during = (Count_end.QuadPart - Count_begin.QuadPart) / freq.QuadPart * 1e9; // 获取以纳秒为单位的时间
测试 了解过程中发现chrono除了system_clock还有一个steady_clock,有了以下测试代码:
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 #include <Windows.h> #include <chrono> #include <iostream> #include <iomanip> int main() { LARGE_INTEGER HPET_begin, HPET_end; auto t_sysclk_begin = chrono::system_clock::now(); auto t_steadyclk_begin = chrono::steady_clock::now(); QueryPerformanceCounter(&HPET_begin); Sleep(1000); auto t_sysclk_end = chrono::system_clock::now(); auto t_steadyclk_end = chrono::steady_clock::now(); QueryPerformanceCounter(&HPET_end); cout << fixed; cout << setprecision(0); auto d = chrono::duration_cast<chrono::nanoseconds>(t_sysclk_end - t_sysclk_begin); // 计算精度设置chrono::nanoseconds,还有microseconds ~ hours,方便! cout << "chrono::system_clock: " << d.count() << "ns" << endl; auto d3 = chrono::duration_cast<chrono::nanoseconds>(t_steadyclk_end - t_steadyclk_begin); cout << "chrono::steady_clock: " << d3.count() << "ns" << endl; LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); cout << "HPET clock: " << (double)(HPET_end.QuadPart - HPET_begin.QuadPart) / freq.LowPart * 1e9<< "ns" << endl; cout << "HPET freq: " << freq.LowPart << "; 理论误差:" << 1.0 / freq.LowPart * 1e9 << "ns" << endl; return 0; }
中间用Sleep(1000)延时1秒,以计时,输出如下:
1 2 3 4 chrono::system_clock: 1000613300ns chrono::steady_clock: 1000610323ns HPET clock: 1000610689ns HPET freq: 2728067; 理论误差:367ns
理论误差是指HPET clock获取的时间理想情况下误差在一次计数的时间(1/freq)以内。
从输出结果可以看到
chrono::system_clock的精度是100ns ,
而chrono::steady_clock的精度达到了1ns 。
推测 通过测试发现steady_clock得到的值多次与HPET计算值相同,其余都是相差一个误差,因此推测steady_clock也是通过高精度计数器计算的。
但是网上找不到相关的说法,只好看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // chrono.h struct steady_clock { // wraps QueryPerformanceCounter …… _NODISCARD static time_point now() noexcept { // get current time const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot const long long _Ctr = _Query_perf_counter(); …… } }; using high_resolution_clock = steady_clock; }
调用了MS的_Query_perf_counter();
看不到具体实现,网上居然也找不到直接的介绍,猜测跟QueryPerformanceCounter
有关,保留猜测。
其实chrono还有一个clock:high_resolution_clock
using high_resolution_clock = steady_clock;
其实就是steady_clock。
总结 chrono::system_clock 可以获取时钟时间,精度到100ns。
chrono::steady_clock 形似高精度计数器,精度1ns。稳定增加,是chrono指定**適於度量間隔**
的clock。
附加 偶然发现boost库有一个计时器cpu_timer,顺便测了一下
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 #include <boost/timer/timer.hpp> #include <Windows.h> #include <iostream> #include <iomanip> int main() { boost::timer::cpu_timer boost_timer; LARGE_INTEGER cpu_begin, cpu_end; boost_timer.start(); QueryPerformanceCounter(&cpu_begin); Sleep(1000); boost_timer.stop(); QueryPerformanceCounter(&cpu_end); cout << fixed; cout << setprecision(0); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); cout << "Boost::cpu_timer: " << boost_timer.format(9) << endl; cout << "Cpu clock: " << (double)(cpu_end.QuadPart - cpu_begin.QuadPart) / freq.LowPart * 1e9<< "ns" << endl; cout << "cpu freq: " << freq.LowPart << "; 理论误差:" << 1.0 / freq.LowPart * 1e9 << "ns" << endl; return 0; }
输出:
1 2 3 Boost::cpu_timer: 1.000058283s wall, 0.00s user + 0.00s system = 0.00s CPU (n/a%) Cpu clock: 1000061949ns cpu freq: 2728067; 理论误差:367ns
比起自己通过chrono获取时间算的误差大多了,足足有 4-10us,官方倒是对此有解释:
Intel Core – 366ns – Some variation, usually in multiples of 366ns reference
通常是366ns的倍数。