QueryPerformanceCounter 需配合 QueryPerformanceFrequency 换算才能获得纳秒级时间差,直接使用 raw 值错误;须验证 freq 非零,且注意两次调用约有 100ns 开销。

Windows 下用 QueryPerformanceCounter 获取纳秒级时间差
在 Windows 上,QueryPerformanceCounter 是唯一能稳定提供微秒甚至纳秒级分辨率的系统 API。它不依赖 CPU 频率缩放或 TSC 不稳定性,适合做精确性能测量。
常见错误是直接把 LARGE_INTEGER 的 raw 值当纳秒用——它只是计数器滴答数,必须配合 QueryPerformanceFrequency 换算:
LARGE_INTEGER freq, start, end; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&start); // ... 代码段 ... QueryPerformanceCounter(&end); double ns = (double)(end.QuadPart - start.QuadPart) * 1e9 / freq.QuadPart;
- 务必检查
QueryPerformanceFrequency返回值是否非零,某些老旧虚拟机可能不支持 - 两次
QueryPerformanceCounter调用本身有约 100ns 开销,测极短函数( - 不要跨进程/线程复用同一个计数器值做比较,不同核心可能有微小偏移(虽极少发生)
Linux 下优先用 clock_gettime(CLOCK_MONOTONIC)
CLOCK_MONOTONIC 是 Linux 最可靠的选择:不受系统时间调整影响,分辨率通常达纳秒级(取决于硬件和内核配置),且调用开销比 gettimeofday 更低。
注意 struct timespec 的 tv_nsec 是 0–999999999,不是微秒;计算差值时要处理借位:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); // ... 代码段 ... clock_gettime(CLOCK_MONOTONIC, &end); int64_t ns = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);
- 某些旧内核(clock_getres 验证
-
CLOCK_MONOTONIC_RAW可绕过 NTP 插值,但可能受硬件时钟漂移影响,一般不需要 - 避免用
CLOCK_REALTIME测性能——系统时间被adjtime或 NTP 调整时会跳变
C++11 std::chrono::high_resolution_clock 的实际行为
别被名字误导:std::chrono::high_resolution_clock 在 Windows MSVC 下底层就是 QueryPerformanceCounter,但在 Linux GCC/Clang 下常退化为 clock_gettime(CLOCK_MONOTONIC) —— 这没问题;真正坑的是某些嵌入式或老平台,它可能直接 alias 到 system_clock(即秒级精度)。
安全做法是运行时探测真实精度:
auto res = std::chrono::high_resolution_clock::period::den /
std::chrono::high_resolution_clock::period::num;
// 如果 res == 1,表示纳秒级;res == 1000000 表示微秒级- 永远不要假设
high_resolution_clock::now()返回值可跨进程比较 - 用
duration_cast<nanoseconds>转换时,注意整数溢出风险(尤其 long long 不足容纳 10 年纳秒数) - 编译器优化可能把空循环或无副作用代码整个删掉,测性能必须让结果参与后续计算(如 volatile 或 asm barrier)
跨平台封装时最易忽略的初始化与校准
高精度计数器本身不校准,但系统启动后首次调用可能有几十纳秒抖动。对要求严苛的基准测试,应预热:
- 在正式测量前,调用计数器 3–5 次并丢弃结果
- 若需多次测量取最小值,每次都要重新调用开始/结束,不能复用初始时间戳
- Linux 上
clock_gettime在某些 CPU(如 AMD Zen2+)上存在跨核调用延迟差异,绑核运行更稳定(pthread_setaffinity_np)
真正的难点不在获取数值,而在确保两次读数之间没有被中断、调度或频率切换干扰——这需要结合 perf event 或 perf_event_open 才能进一步验证。


















