一、了解RDTSC指令
rdtsc指令, 该指令返回CPU自启动以来的时钟周期数;该时钟周期数,即处理器的时间戳。
在CPU通电启动后,首先会重置EDX和EAX,在每个时钟周期上升或下降沿到来时,会自动累计周期数,并被记录到EDX和EAX寄存器中,EDX是高位,EAX是低位。
rdtsc指令就是从该寄存器中进行获取的。
周期和频率的关系公式:T(周期)=1/f(频率)
如CPU频率f为1GHz,则其时钟周期T=1/1GHz秒,意味着每隔T秒,CPU完成一个最基本的动作,并在寄存器中,对周期数加1。
故,假设当前时钟周期数为m,则可计算出CPU自启动后,累计运行时间X=m*T,整理下:
X=m/f
时钟周期与时钟频率互为倒数, 我们可以找出以下关系:
时钟频率 | 时钟周期 |
---|---|
1Hz | 1s |
1KHz | 1ms |
1MHz | 1μs |
1GHz | 1ns |
注意计算时,单位要搭配好,比如:
- f为Hz时,计算得到的X为秒;
- f为GHz时,计算得到的X为纳秒。
在 Windows 用 QueryPerformanceCounter 和 QueryPerformanceFrequency,Linux 下用 POSIX 的 clock_gettime 函数,RDTS 耗时最少。
小结:
CPU累计运行时间=时钟周期数/CPU频率
二、RDTSC指令在win和linux上的实现
我们已经知道RDTSC可以获取CPU运行时间,另外win平台上也已经有了一个类似的函数:
DWORD GetTickCount();
该函数返回值是系统启动后经过的毫秒数,最长为49.7天。
其微软帮助为:
GetTickCount function (sysinfoapi.h) - Win32 apps | Microsoft Docs
三、win和linux下RDTSC的实现
#include <stdint.h>
// rdtsc
#if _WIN32
#include <intrin.h>
uint64_t rdtsc() // win
{
return __rdtsc();
}
#else
uint64_t rdtsc() // linux
{
unsigned int lo, hi;
__asm__ volatile ("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}
#endif
四、利用chrono统计毫秒,微妙,纳秒运行时间
void CalRunTime() {
auto t1=std::chrono::steady_clock::now();
//run code
auto t2=std::chrono::steady_clock::now();
//秒
double dr_s=std::chrono::duration<double>(t2-t1).count();
//毫秒级
double dr_ms=std::chrono::duration<double,std::milli>(t2-t1).count();
//微妙级
double dr_us=std::chrono::duration<double,std::micro>(t2-t1).count();
//纳秒级
double dr_ns=std::chrono::duration<double,std::nano>(t2-t1).count();
}
chrono中有三种时钟:system_clock,steady_clock和high_resolution_clock。每一个clock类中都有确定的time_point, duration, Rep, Period类型。
system_clock是不稳定的。因为时钟是可调的,即这种是完全自动适应本地账户的调节。这种调节可能造成的是,首次调用now()返回的时间要早于上次调用now()所返回的时间,这就违反了节拍频率的均匀分布。稳定闹钟对于超时的计算很重要,所以C++标准库提供一个稳定时钟 std::chrono::steady_clock。std::chrono::high_resolution_clock 是标准库中提供的具有最小节拍周期(因此具有最高的精度的时钟)。
上文所说time_since_epoch(),以及将要介绍的now()函数的返回值都依赖于时钟的精度,测试时钟的精度的一种方法就是:
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
cout << "system clock : ";
cout << chrono::system_clock::period::num << "/" <<
chrono::system_clock::period::den << "s" << endl;
cout << "steady clock : ";
cout << chrono::steady_clock::period::num << "/"
<< chrono::steady_clock::period::den << "s" << endl;
cout << "high resolution clock : ";
cout << chrono::high_resolution_clock::period::num << "/"
<< chrono::high_resolution_clock::period::den << "s" << endl;
system("pause");
return 0;
}
windows系统的测试结果是system_clock的精度是100纳秒,而high_resolution的精度是1纳秒,对于程序来说,一般毫秒级就够了,所以说chrono提供的时钟精度绰绰有余。
win下,__rdtsc()函数的微软帮助为:
linux下,则需要内联汇编来实现。
RDTSC指令结合CPU频率,也可以用来计算代码段的执行时间。
参考链接: