<!-- curated-deep-dive -->
`folly/chrono/Hardware.h` 深度解读
-
源码行数:155
-
源文件 SHA-256:`5634d5acaf5b52b780afc7f43234699f13cd6a7e9786a63bd0084d4ad83b070f`
-
文档类型:人工精读版
本文件提供两级低成本时间戳:`hardware_timestamp()` 尽量直接读取硬件计数器,允许编译器/CPU 与周边指令重排;measurement_start/stop 则使用编译器屏障、LFENCE 和 RDTSC/RDTSCP,构造适合微基准的测量边界。
返回值是平台原生 tick,不是纳秒,也不保证跨平台相同频率。调用者通常只在同一平台上做差值。
第 1--39 行:平台准备
第 1--22 行为许可证、pragma、Portability、chrono 和整数。第 24--25 行引用精确测量序列的设计来源。
第 27--37 行只为非 Clang MSVC x86/x64 手工声明并标记 RDTSC、RDTSCP、编译器 barrier 与 LFENCE intrinsic;其他编译器使用 builtin 或内联汇编。第 39 行进入 folly。
第 41--55 行:未序列化时间戳
-
MSVC x86/x64:`__rdtsc()`;
-
GCC/Clang i386/x64:`__builtin_ia32_rdtsc()`;
-
非移动 AArch64:读取 `cntvct_el0` 虚拟计数器;
-
其他平台:退化为 steady_clock epoch count。
该函数没有 memory clobber 或 fence,适合退避、粗略采样等低开销场景,不适合严格界定一小段指令。`SharedMutex`、`DistributedMutex` 和 `TurnSequencer` 用它计算自旋/等待经过 tick;相关代码也防御计数器倒退或回绕。
第 57--78 行:测量边界契约
注释区分三种保证:阻止编译器把指令跨调用移动;阻止 CPU 将 load 与时间戳流水重叠;但不保证先前 store 已全局可见。它测量典型计算/加载区域,而不是完整内存系统排空。第 77--78 行先声明 start/stop,再在下方内联定义,方便文档与签名集中呈现。
第 80--116 行:measurement start
MSVC 路径在 RDTSC 前后放 `_ReadWriteBarrier` 与 `_mm_lfence`。编译器 barrier 管编译期重排,LFENCE 管 CPU 执行顺序。
GCC x64 路径内联汇编执行:
```text
lfence → rdtsc(edx:eax) → edx左移32并OR到rax → lfence
```
输出约束把 ret 绑定 rax,clobber rdx、条件码和 memory;memory clobber 是编译器屏障。Clang 11+ 支持 `asm volatile inline`,旧版退回 `asm volatile`。非 x64 使用 steady_clock 后备,这时 start/stop 只有标准函数调用所提供的近似边界,不等价 x86 指令序列。
第 118--153 行:measurement stop
停止边界用 RDTSCP:它等待更早指令达到所需完成顺序后再读计数器,并额外写 ECX/AUX;随后 LFENCE 阻止后续指令提前。
MSVC 传入 aux 地址并忽略 CPU 标识。GCC 汇编将 rcx 加入 clobber,组合 edx:eax 与 start 相同。非 x64 同样退化为 steady_clock。
典型用法必须配对:
```cpp
auto begin = hardware_timestamp_measurement_start();
measured_work();
auto end = hardware_timestamp_measurement_stop();
auto ticks = end - begin;
```
start/stop 自身有成本,极短区域应通过空测量校准或基准对照。第 155 行关闭命名空间。
调用证据
`ChronoBench.cpp` 导出配对测量符号并分别基准三种 API;互斥量/序列器使用未序列化版本降低热路径开销。由此可见两个接口族不是强弱替换关系,而是"最低开销进度计数"和"明确测量边界"的不同用途。