c++oding="utf-8" ?>
不能用std::chrono::steady_clock或system_clock获取纯cpu时间,因它们测的是wall-clock;应调用平台api:linux用clock_gettime(clock_process_cputime_id)或clock_thread_cputime_id,windows用getprocesstimes,再将纳秒值构造为std::chrono::nanoseconds。

如何用 std::chrono 获取纯 CPU 时间(非 wall-clock)
不能直接用 std::chrono::steady_clock 或 system_clock,它们测的是真实流逝时间,会把线程休眠、系统调度等待都算进去。要真正反映“CPU 干活花了多久”,得用 std::chrono::high_resolution_clock 配合进程/线程的 CPU 时间接口 —— 但标准 C++ 没提供,必须依赖平台 API。
Linux 下最可靠的是 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...);Windows 下用 GetProcessTimes。跨平台封装时,别硬套 std::chrono 的 duration 转换逻辑,先拿到纳秒级原始值再构造 duration 更安全。
- Linux:确保链接
-lrt(尤其在旧 glibc 环境下) - Windows:
GetProcessTimes返回的是 100ns 单位,需乘以 100 得到纳秒,再用std::chrono::nanoseconds构造 - 避免在计时区间内调用可能触发页错误或锁竞争的代码,否则 CPU 时间会包含内核态开销,失真
Linux 下用 clock_gettime 实现高精度 CPU 计时器
这是目前 Linux 上最轻量、最准的方式,开销极小,且能精确到纳秒级(取决于硬件支持)。注意它返回的是整个进程的累计 CPU 时间,不是单线程的 —— 如果你在多线程程序里只关心某段代码所在线程的 CPU 消耗,得改用 CLOCK_THREAD_CPUTIME_ID。
#include <time.h>
#include <chrono><p>struct cpu<em>timer {
struct timespec start</em>;
void start() { clock_gettime(CLOCK_PROCESS_CPUTIME<em>ID, &start</em>); }
std::chrono::nanoseconds elapsed() const {
struct timespec end_;
clock_gettime(CLOCK_PROCESS_CPUTIME<em>ID, &end</em>);
return std::chrono::nanoseconds((end_.tv<em>sec - start</em>.tv<em>sec) * 1000000000LL +
(end</em>.tv<em>nsec - start</em>.tv_nsec));
}
};</p></chrono></time.h>
-
CLOCK_PROCESS_CPUTIME_ID统计所有线程用户态 + 内核态 CPU 时间总和 - 如果只测当前线程,把
CLOCK_PROCESS_CPUTIME_ID换成CLOCK_THREAD_CPUTIME_ID,但后者在某些容器环境(如 Docker 默认 cgroup 配置)下可能被禁用 - 不要在
elapsed()里重复调用clock_gettime做减法再转duration—— 先算好纳秒整数再构造,避免浮点误差或隐式转换开销
Windows 下用 GetProcessTimes 替代方案
Windows 没有 POSIX 的 clock_gettime,GetProcessTimes 是等效选择。它返回四个 FILETIME(创建、退出、内核态、用户态),我们只需后两者之和。FILETIME 是 100ns 单位,所以要 ×100 得到纳秒,再喂给 std::chrono::nanoseconds。
#include <windows.h>
#include <chrono><p>struct cpu<em>timer {
FILETIME kernel</em>, user<em>;
void start() { GetProcessTimes(GetCurrentProcess(), nullptr, nullptr, &kernel</em>, &user_); }
std::chrono::nanoseconds elapsed() const {
FILETIME kernel_end, user_end;
GetProcessTimes(GetCurrentProcess(), nullptr, nullptr, &kernel_end, &user_end);
ULARGE_INTEGER k, u;
k.u.LowPart = kernel_end.dwLowDateTime; k.u.HighPart = kernel_end.dwHighDateTime;
u.u.LowPart = user_end.dwLowDateTime; u.u.HighPart = user_end.dwHighDateTime;
ULARGE<em>INTEGER k0, u0;
k0.u.LowPart = kernel</em>.dwLowDateTime; k0.u.HighPart = kernel<em>.dwHighDateTime;
u0.u.LowPart = user</em>.dwLowDateTime; u0.u.HighPart = user_.dwHighDateTime;
return std::chrono::nanoseconds(static_cast<long long>((k.QuadPart - k0.QuadPart + u.QuadPart - u0.QuadPart) * 100));
}
};</long></p></chrono></windows.h>
- FILETIME 是 64 位整数,但 Windows SDK 定义为两个
DWORD,必须用ULARGE_INTEGER或手动拼接,否则高位截断 - 该 API 在 UWP 或沙盒进程里可能失败(返回 0),需检查
GetLastError()==ERROR_ACCESS_DENIED - 不要用
GetTickCount64或QueryPerformanceCounter替代 —— 它们是 wall-clock,不反映 CPU 占用
为什么不用 std::clock()?
std::clock() 看似最简单,但它在多数实现中只是对 clock_gettime(CLOCK_PROCESS_CPUTIME_ID) 或 GetProcessTimes 的薄封装,问题在于:POSIX 规定它返回“处理器时间”,但 C++ 标准只保证“大致比例”,且其精度通常只有毫秒级(glibc 下常为 10ms),还可能受 CLOCKS_PER_SEC 宏误导 —— 它未必等于实际分辨率。
- Linux glibc 中
std::clock()常基于getrusage(RUSAGE_SELF),而getrusage在 cgroup v2 下可能不准 - Clang/libc++ 在 macOS 上用
mach_absolute_time,但默认映射为 wall-clock,除非显式配置 - 实测:同一段密集计算,
std::clock()可能比clock_gettime少报 5–15% CPU 时间,尤其在短于 10ms 的场景下
真正需要量化 CPU 消耗时,绕过 std::clock() 直接调用底层 API,是最稳妥的做法。跨平台项目建议用宏或构建系统区分,而不是试图抽象出一个“通用 clock”类型 —— 底层语义差异太大,强行统一反而容易埋坑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











