定时器: 石心
在看 Node http 模块文档的时候, 才留意到
server.timeout
这个属性, 本想简单介绍一下, 但是在梳理过后发现关于timeout
有庞大的内容支撑:server.timout -> node core timers -> uv timers -> linux msleep/hrtimer -> clocksource -> tsc -> cmos rtc -> clock cycle
, 所以拆分成几部分大致做下介绍, 期望定时器系列结束之后, noder 能够大致明白: clock cycle 是如何驱动 linux的msleep/hrtimer;linux 的 timers 与 uv timers 的关系;node timers 与 uv timers的关系。
定时器是编程中运用最广的特性之一, 那么定时器实现的基本原理是什么呢?无论多么复杂计算机,当一层层退去她的外衣,她的内核只是一块可以发出固定频率脉冲的石心(石英), 好比人类的心脏,通过跳动给整个躯体提供能量和信号。
周期 与 精度
时钟周期, CPU周期[min(取指令)], 指令周期[取指令+执行指令], 微指令周期
在 intel haswell 微架构内核架构中, 一条指令通常会被分解为多条微指令, 一个时钟周期,理论上最多可以并行执行8个微指令。
时钟周期与时间关系:
频率(HZ) | 1 | 1K | 1M | 1G | 1000G |
---|---|---|---|---|---|
时间(/Hz) | 1s | 1ms | 1us | 1ns | 1ps |
目前普通的计算机CPU频率都在GHz级别, 以鄙人电脑为例: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
, 意味着每秒钟有 1.6e9 次时钟周期。一个时钟周期大约为 0.625ns
,所以目前来说普通的机器的定时器精度最多只能达到 ns 纳秒级别,那精度到底是 1n,10ns, 100ns 级别?要解决这个问题,需要从(微)指令的周期说起。在 linux 内核中 ,timer 最底层(本质是计数器
)的一个指令是 ADD 1
, 纵然在流水线
的加持下,也避免不两个最基本的且具有先后管线的微指令: 运算和回写,所以最小的运算操作都需要0.625ns*2 = 1.25ns
,那么目前定时器最大应该在几个纳秒左右。
原本期望能够大概算出 linux 的 hrtimer 的精度范围, 实在因为 CPU 和 hrtimer 太复杂,个人知识有限,无法估计出来。目前 intel 最高睿频 10GHz 的情况下,如果如果能在10个时钟周期内完成一次while-schedule循环,也许。。。
本质 --- while + schedule
讲一个笑话: 双11凌晨开始大促销
while (true) {
const now = new Date();
if (now === '双11凌晨') {
break;
}
}
开始优惠();
虽然该程序或许存在一点点阻塞问题,但不可否认可以实现定时, 其实这就是定时器的本质。
加入非阻塞
while (true) {
const now = new Date();
if (now === '双11凌晨') {
break;
}
schedule();// 调度让出 cpu 操作
}
开始优惠();
支持多个定时器
const timers = Some Data Structur[timer];
。。。
while (true) {
const latest_timer = timers.peek(); // get the latest timer
if (latest_timer && latest_timer === '双11凌晨') {
break;
}
schedule();// 调度让出 cpu 操作
}
开始优惠();
剩余部分就是考虑在什么场景下,采用什么数据结构
来保存定时器数据,从而可以实现C|U|R|D的最优
(通常是时间最优)
参考
CPU流水线的探秘之旅
64-ia-32-architectures-optimization-manual