Hardware.h

<!-- 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;互斥量/序列器使用未序列化版本降低热路径开销。由此可见两个接口族不是强弱替换关系,而是"最低开销进度计数"和"明确测量边界"的不同用途。

相关推荐
hintonic1 天前
FindFixed.h
folly