首页 > 通过 RDTSC 指令从 CPU 寄存器中直接获取系统时钟

通过 RDTSC 指令从 CPU 寄存器中直接获取系统时钟

很多时候我们使用函数 gettimeofday 以及 clock_gettime 作为我们获取 wall lock的时钟函数。

因为这两种函数是 glibc 提供的用户封装,简单易用,而且能够精确到 ns,对于大多数的时钟需求场景都已经够用了。

但是如果 我们的应用 调用时钟频繁 且 对 多线程场景下时钟的单调性要求不是特别高的时候 ,以上提到的两个 glibc 函数的开销其实是有点大的。

  • gettimeofday 底层是会调用到系统调用 sys_gettimeofday,执行逻辑会陷入内核,将内核保存的 walllock 和 jiffies 做一个综合的精度计算,将计算结果从内核态拷贝到用户态,交给timeval,这整个过程可以说开销是比较大的。(需要系统调用)
  • clock_gettime,这个函数同样的需要执行一个系统调用 ,并且做 精度处理之后才返回给用户态。

举个例子,我们分布式系统中的打点系统可以说非常重要,事关整个系统健康情况的展示,能够帮助我们提前或者及时得准确发现系统中潜在的问题, 但是因为这一些打点需要在关键路径上,往往对整个系统性能会有一定的损耗。而这一些打点 并不影响整个分布式系统中 为分布式事务选择的时钟(可以用两套时钟),只需要能够计算出一个操作前后的时间差即可,保持单线程内的单调性即可。

所以,如果我们的系统中所有的时钟都采用 glibc 实现的两种方式,对于频繁的打点系统来说 代价实在是有点大,而且对关键路径的性能都会有一定的影响。

今天介绍一个可以从 CPU 寄存器中直接获取 walllock 的汇编指令 RDTSC

我们目前大多数的服务器系统是 AMD64 , x86_32/x86_64 位,可以通过如下 代码获取:

#include 
#include int main() { uint64_t a,d,t;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));t = ((d << 32) | a);printf("%lu
",t);return 0;
}

拿到的 walllock 是 自1970.1.1 到现在 的CPU周期数,对于 GHZ 的CPU 来说单位就是ns。

我们的服务器主板有一个即使机器断电重启也不会重设的存储单元 RTC,它会将当前机器到 1970.1.1 经过的时间转化为当前机器的CPU时钟周期并存储下来。

我们的CPU 在完成一个时钟周期之后会在 MSR(model-specific register) 寄存器中存储计数(64位),我们的 RDTSC (read Time-Stamp Counter) 指令就是直接从 MSR 寄存器中取出计数信息,其中的 高32位 放在 edx 寄存器中,低 32位放在eax 寄存器中。

这也就是为什么 我们在AMD64 下执行这个指令需要在取到结果之后对存储高 32位数据 的 d 变量做一个移位。

这样我们就可以完整得实现一个 在AMD64 架构下的 rdtsc 指令的时间统计:

#include 
#include static inline 
uint64_t rdtsc_time() { uint64_t a,d;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));return ((d << 32) | a);
}static inline 
uint64_t clock_to_nsec(uint64_t begin, uint64_t end) { double clock_diff;//  这里做一些简单的单调性处理, 防止 end < begin.if (end < begin)return (0);clock_diff = (double)(end - begin);return ((uint64_t)(clock_diff);
}int main() { uint64_t start_ns = rdtsc_time();// do some code......uint64_t end_ns = rdtsc_time();fprintf(stdout, "code execute time(ns) : %lu
", clock_to_nsec(start_ns, end_ns));return 0;
}

为了兼容不同的平台,像 i386 以及 arm 平台,所以,我们需要变更 rdtsc_time 函数为如下形态(不同架构下的获取时钟指令有一些差异):

static inline uint64_t
rdtsc_time(void)
{ 
#if defined(__i386)  // intel 80386 架构{ uint64_t x;__asm__ volatile("rdtsc" : "=A"(x));return (x);}
#elif defined(__amd64) // amd x86_64架构{ uint64_t a, d;__asm__ volatile("rdtsc" : "=a"(a), "=d"(d));return ((d << 32) | a);}
#elif defined(__aarch64__) // arm 64为架构{ uint64_t t;__asm__ volatile("mrs %0,  cntvct_el0" : "=r"(t));return (t);}
#elsereturn (0);
#endif
}

参考:

https://www.amd.com/system/files/TechDocs/24594.pdf

https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf

更多相关:

  • 英语的重要性,毋庸置疑!尤其对广大职场人士,掌握英语意味着就多了一项竞争的技能。那,对于我们成人来说,时间是最宝贵的。如何短时间内在英语方面有所突破,这是我们最关心的事情。英语学习,到底有没有捷径可以走,是否可以速成?周老师在这里明确告诉大家,英语学习,没有绝对的捷径走,但是可以少走弯路。十多年的教学经验告诉我们,成功的学习方法可以借...

  • 展开全部 其实IDLE提供了一个显32313133353236313431303231363533e78988e69d8331333365663438示所有行和所有字符的功能。 我们打开IDLE shell或者IDLE编辑器,可以看到左下角有个Ln和Col,事实上,Ln是当前光标所在行,Col是当前光标所在列。 我们如果想得到文件代码...

  • 前言[1]从 Main 方法说起[2]走进 Tomcat 内部[3]总结[4]《Java 2019 超神之路》《Dubbo 实现原理与源码解析 —— 精品合集》《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》《Spring MVC 实现原理与源码解析 —— 精品合集》《Spri...

  • 【本文摘要】【注】本文所述内容为学习Yjango《学习观》相关视频之后的总结,观点归Yjango所有,本文仅作为学习之用。阅读本节,会让你对英语这类运动类知识的学习豁然开朗,你会知道英语学习方面,我们的症结所在。学习英语这类运动类知识,需要把握四个原则第一,不要用主动意识。第二,关注于端对端第三,输入输出符合实际情况第四,通过多个例子...

  • 点云PCL免费知识星球,点云论文速读。文章:RGB-D SLAM with Structural Regularities作者:Yanyan Li , Raza Yunus , Nikolas Brasch , Nassir Navab and Federico Tombari编译:点云PCL代码:https://github.co...

  • 文章:GVINS: Tightly Coupled GNSS-Visual-Inertial Fusion for Smooth and Consistent State Estimation作者:Shaozu Cao, Xiuyuan Lu, and Shaojie Shen代码:https://github.com/HKUST-...

  • 数据存储系统的经典书籍: 从数据系统的特性开始,先讲单机存储引擎 再到 分布式存储系统,最后到一些数据流的处理方式,作者深入浅出,译者更是精雕细琢,本书需要细品。 将持续阅读整理,先从理论走一轮,再找一些系统做一做实验。...

  • 常见的linux进程状态如下: 关于源文件xmid,可以从Mind-Mapping获取 这里借助进程状态来描述一下linux系统中的平均负载的概念 当我们感觉到系统变慢时,通常通过top和uptime命令来了解系统的负载情况 [root@pub-ncpu-ndb0 ~]# uptime21:06:13 up 8 days, 7:...

  • 围绕分布式存储(ceph)绘制的技能图谱可参考分布式存储ceph 技能图谱 相关的原始编辑文件可以从github-mindMapping下载 如有缺失、不足之处欢迎指正 CEPH架构 关于系统架构,这里主要是将CEPH融入操作系统架构之中 且是根据L版本进行绘制的 关于文件系统 :因为bluestore跳过了本地文件系统,同时封...

  • linux 系统崩溃完全没有操作空间的系统修复 1、通过U盘系统启动 2、修复文件系统 https://editor.csdn.net/md/?articleId=106213788 此时硬盘会被挂在到U盘系统下作为一个目录, 例如/dev/sda2 修复它: fsck -y /dev/sda2 很多时候都有效 3、修复g...