应使用clock_gettime(clock_monotonic, &)测墙上时间,因其单调不跳变、反映真实等待时长;clock_process_cputime_id测进程总cpu时间;/proc/self/task/[tid]/stat与schedstat分别分析线程级cpu分布及调度延迟。

Linux 下统计 C++ 多线程程序的运行时间,不能只用 clock() 或单一线程的 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) —— 它们要么只反映主线程 CPU 时间,要么在多线程下严重低估总开销。真正有意义的“程序运行时间”分三类:墙上时间(wall-clock)、进程总 CPU 时间、各线程调度延迟。选错指标会导致性能分析完全失真。
用 clock_gettime(CLOCK_MONOTONIC, &) 测墙上时间
这是最直观的“用户等了多久”,不受线程数、休眠、I/O 阻塞影响,只随系统滴答走。
-
CLOCK_MONOTONIC从系统启动开始计时,不跳变、不回拨,适合测端到端耗时 - 必须两次调用取差值:
start放在 main 开头,end放在 main 返回前(或用 RAII 封装) - 别用
std::chrono::steady_clock替代——它底层可能映射到CLOCK_MONOTONIC,但精度和行为依赖 libc 实现;直接调clock_gettime更可控 - 示例中常见错误:在某个子线程里调用
clock_gettime然后打印,误以为是“程序运行时间”——其实只是那个线程启动到此刻的单调时间,和主流程无关
用 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &) 测进程总 CPU 时间
这个值才是“所有线程加起来实际占了多少 CPU 时间”,单位纳秒,是性能优化的核心指标。
Linux 性能分析与调优专家,覆盖 CPU、内存、磁盘 I/O、网络、内核参数、编译优化、容器/K8s。适用场景:系统卡顿/高负载、内存不足/OOM/Swap 高、CPU 异常/iowait 高。
- POSIX 要求它返回整个进程(所有线程)的用户态 + 内核态 CPU 时间总和,但 glibc 当前实现中,它返回的是**调用线程所在进程的累计值**——所以只要你在主线程调一次,就拿到全进程总和
- 注意:它不包含子进程时间(
fork后子进程独立计时),也不含内核中断/软中断等非进程上下文时间 - 别和
CLOCK_THREAD_CPUTIME_ID混用:后者只返回当前线程的 CPU 时间,多线程下 sum 所有线程结果 ≈CLOCK_PROCESS_CPUTIME_ID,但没必要——前者更轻量、更准确 - 如果编译报错 undefined reference to
clock_gettime,加链接选项-lrt
用 /proc/self/task/[tid]/stat 拆解各线程 CPU 时间分布
当你发现进程总 CPU 时间高,但不知道哪条线程在吃资源,就得下钻到线程级——/proc/self/task/[tid]/stat 是唯一能分离用户态/内核态且带线程粒度的原生接口。
- 先用
syscall(SYS_gettid)获取当前线程 tid(不是pthread_self()),再拼路径:/proc/self/task/12345/stat - 字段按空格分割,第 14 位是
utime(用户态 jiffies),第 15 位是stime(内核态 jiffies);jiffies 要除以sysconf(_SC_CLK_TCK)(通常是 100)转成秒 - 必须两次采样做差:单次读出来的是累计值,对分析无意义;间隔至少 100ms,否则差值为 0 或噪声大
- 频繁读
/proc文件有 I/O 开销,不适合高频采样(如每毫秒一次);若需持续监控,建议用 eBPF 替代
用 /proc/self/task/[tid]/schedstat 看线程调度延迟
CPU 时间高 ≠ 程序写得差,可能是调度不及时。这时候要看 schedstat——它暴露线程被就绪但没抢到 CPU 的等待时间。
- 文件格式固定三列:
nr_switches runtime_ns wait_sum_ns,关键看第三列wait_sum_ns(总等待纳秒数) - 该文件存在前提:内核编译开启
CONFIG_SCHEDSTATS=y,且启动参数未禁用(如没加schedstats=0);主流发行版默认开启,但容器环境可能被裁剪 - 同样必须两次采样:记录上一次的
wait_sum_ns和时间戳,本次减去上次,再除以时间差,得到“单位时间内平均等待纳秒数” - 常见误判:看到某线程
wait_sum_ns很大就认为它卡住了——其实可能是它生命周期长、唤醒次数多;要结合nr_switches看等待/调度比,才有业务意义
真正难的不是读哪个文件或调哪个函数,而是理解每个数值背后的调度语义:墙上时间告诉你用户感知,进程 CPU 时间告诉你计算负载,线程级 utime/stime 告诉你用户/内核侧偏重,schedstat 才揭示你是否被调度器“欺负”。四者缺一不可,但顺序不能乱——先看墙上时间是否超标,再查 CPU 时间是否匹配,最后才下钻到线程和调度层。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










