QueryPerformanceFrequency返回的是高性能计数器(如TSC或HPET)的固定基准频率,而非CPU主频;因其由系统启动时探测确定,受硬件支持、电源策略及TSC是否恒定影响,与CPU动态睿频、降频无关。

为什么 QueryPerformanceFrequency 返回的值可能不等于 CPU 主频
Windows 下获取高精度 Tick 频率最常用的是 QueryPerformanceFrequency,但它返回的不是 CPU 基础时钟频率,而是底层性能计数器(通常是 TSC 或 HPET)的每秒计数次数。这个值由系统在启动时探测并固定,受硬件和电源管理策略影响——比如 Intel 的 invariant TSC 启用后,TSC 频率才稳定且接近标称主频;否则可能被降频、跳变甚至暂停。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 始终调用
QueryPerformanceFrequency获取实际频率,不要硬编码(如 10000000 或 2.4e9) - 检查返回值是否为
TRUE,失败时回退到GetTickCount64或std::chrono::steady_clock - 避免在不同 CPU 核心间迁移线程后直接复用旧频率值(极少数老平台存在 per-core TSC drift)
Linux 下用 clock_gettime(CLOCK_MONOTONIC, &ts) 但无法直接读取频率
Linux 没有等价于 QueryPerformanceFrequency 的用户态 API。内核的 CLOCK_MONOTONIC 底层通常基于 hpet、tsc 或 acpi_pm,其分辨率由硬件决定,但用户无法直接查询该计数器的原始频率。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 若需估算,可反复调用
clock_gettime测量最小可观测时间差(例如循环 1000 次取最小 delta),再反推“有效频率”(仅作参考,非真实硬件频率) - 更可靠的做法是依赖
std::chrono::high_resolution_clock::period:它在大多数现代 libc++/libstdc++ 实现中会映射为纳秒级精度(如std::ratio),但注意它不保证等于底层计数器频率 - 不要尝试读取
/sys/devices/system/clocksource/clocksource0/current_clocksource后去查文档对应频率——不同 clocksource 的实现差异极大,且无统一换算接口
std::chrono::high_resolution_clock::period 不代表硬件 Tick 频率
C++ 标准只规定 high_resolution_clock 是“系统可用的最高精度时钟”,但未定义其底层来源或是否稳定。它的 period 是编译期常量,反映库实现的**标称精度**,而非运行时实际计数器频率。
常见误解与应对:
- 在 Windows MSVC 中,它通常基于
QueryPerformanceCounter,period多为std::ratio(即 1ns),但真实分辨率取决于QueryPerformanceFrequency返回值(如 2.5GHz → 理论最小间隔 0.4ns) - 在 Linux GCC 中,它可能映射为
CLOCK_MONOTONIC,period同样标称为 1ns,但实际抖动可能达微秒级(尤其在虚拟机或容器中) - 跨平台代码中,别用
period::den除法来算 Tick 数;应统一用duration_cast转换,让标准库处理底层缩放
真正需要硬件 Tick 频率时,只能平台特异性处理
如果你在做性能采样、硬件级延时控制或时间戳对齐(比如音视频同步、FPGA 时间戳校准),就必须绕过 C++ 抽象,直连系统能力。
关键点:
- Windows:调用
QueryPerformanceFrequency(&freq),freq.QuadPart即每秒计数,后续用QueryPerformanceCounter差值除以它得秒数 - Linux:可通过
rdtsc指令 +cpuid校准(需 root 权限和perf_event_paranoid设置),但已被现代内核限制;更可行的是读取/proc/cpuinfo的cpu MHz字段(注意这是当前 P-state 频率,非 invariant TSC) - macOS:用
mach_timebase_info_data_t+mach_timebase_info,它给出numer/denom比例,用于将mach_absolute_time()结果转为纳秒——这才是你真正能稳定依赖的“频率换算系数”
最易被忽略的是:即使拿到频率,两次 QueryPerformanceCounter 或 mach_absolute_time 的差值也不等于真实流逝时间,中间可能有中断延迟、调度抢占或计数器暂停。真要精确,得结合硬件 PMU 或 kernel tracepoints。


















