Appearance
计时与测量:RDTSC、clock、benchmark 精度陷阱
测时是性能工程的基础。面试偶尔直接考“你怎么精确量一段代码耗时”,须分清三种时钟与陷阱。
一、C++ 常用时钟
cpp
#include <chrono>
auto t0 = std::chrono::steady_clock::now(); // 单调(不受 wall 调整影响)✅ 基准
auto ms = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now()-t0).count();
std::chrono::system_clock // 墙上时间,可被 NTP/用户改 → 不适合测时差
std::chrono::high_resolution_clock // 通常 == steady_clock 的实现;勿当“更准”魔法结论:基准用 steady_clock(保证单调不随系统时间跳);steady_clock 精度随平台(Linux 常给 ns/µs)。
二、RDTSC(Read Time-Stamp Counter)
x86:读 CPU cycle 计数器的指令。直觉用来测“多少 cycle”。
cpp
#include <x86intrin.h>
unsigned long long s = __rdtsc();
...被测代码...
unsigned long long e = __rdtsc();陷阱(必答):
- 乱序执行:rdtsc 之间代码可能被 OoO 提升/移出,测得不准 → 用
__rdtscp(serialize + 读到核心 id)或mfence/lfence围。 - 频率可变(turbo/下降):cycle 数 不能直接换成固定纳秒;现代机器频率动态。
- 核心间计数不一致(不同核 TSC 可漂移/锁频不同)。 因此交叉验证用 steady_clock 纳秒换算,RDTSC 只用于“相对 cycle 估算”且封好。
三、测量十诫(关键考点)
- 结果可观察,防编译器把循环删光(volatile sink / 输出累加 / asm 阻止优化)。
- 预热:跑几遍再计时,排除首访 cache cold & 动态加载。
- 多次重复取中位/最小(去掉系统噪声尖峰),别用单次墙钟。
- 分多次,避免计数溢出、避免跨 big interval。
- 关后台抖动:绑核、关 turbo(若可)、空闲机器。
- 注意频率与节能:P 状态会影响结果 → 相对对比或固定环境。
- 测量边界含函数调用栈切换(fn enter)抖动小但存在。
- 微基准结果不代表真实工作负载 → 结合真实 scenario 验证 。
- Google Benchmark 能自动做预热/重复/统计 → 用它,别手写轮子。
- 报告单位一致(ns/µs/cycles),标注环境(CPU、编译器、-O、操作系统)。
四、相关“不准”题破解
- “我测出来似乎没差 / 乱跳”:先看频率、随机系统噪声、cache 未预热、或编译器把被测代码优化掉(未观察)。
- 想测 kernel 耗时变化 → steady_clock + 预热 + 多次取 min + 绑核,再与 perf 吞吐对照。
- 单调 vs 墙钟:凡是测 delta 都用 steady,避免 NTP 跳。