前言最近做算法性能测试时,遇到一个很典型的问题:测试程序跑起来后,怎么知道它占了多少 CPU?用 top 看总感觉不够精确,有没有更靠谱的办法?这个问题看似简单,实则踩了不少坑。网上大部分教程都在讲 top、pidstat 怎么看 CPU 占用,但不同场景下该用哪个工具、结果怎么解读、有哪些坑,往往语焉不详。这篇文章把整个排查过程整理出来,包括:Linux 查看 CPU 占用率的常用方法及适用场景怎么找到测试程序对应的进程短生命周期程序怎么测才靠谱(核心)结果解读与常见坑一、Linux 查看 CPU 占用率的常用方法先快速过一遍基础工具,按场景选:场景推荐命令日常排查top/htop看多核负载是否均衡mpstat -P ALL 1找吃 CPU 的进程ps aux --sort=-%cpu | head或pidstat -u 1历史/趋势分析sar -u写脚本取数值读/proc/stat或解析mpstat1. top —— 最常用top第一行%Cpu(s)显示整体占用:us:用户态sy:内核态id:空闲率(CPU 占用率 ≈ 100 - id)wa:IO 等待st:被虚拟化偷走的时间按1展开每个核心,按P按 CPU 排序,按q退出。2. mpstat —— 看每个核心mpstat-PALL1# 每 1 秒刷新,显示所有核心需安装sysstat。%idle是空闲率,100 - %idle即该核心占用率。3. vmstat —— 整体概况vmstat15# 每秒采样,共 5 次关注us、sy、id、wa列。4. ps —— 查某进程psaux--sort=-%cpu|head-10# 按 CPU 占用降序取前 10ps-p<PID>-o%cpu,cmd# 查看指定进程5. pidstat —— 按进程持续统计pidstat-u15# 每秒采样,共 5 次pidstat-u-p<PID>1# 只看某个进程6. sar —— 历史数据sar-u15# 实时sar-u-f/var/log/sa/sa15# 某天的历史数据7. 脚本里直接取数值读/proc/stat两次采样求差值:readcpu a b c idle rest</proc/statt1=$((a+b+c+idle));i1=$idlesleep1readcpu a b c idle rest</proc/statt2=$((a+b+c+idle));i2=$idleecho"CPU 占用率:$((100-(i2-i1)*100/(t2-t1)))%"二、怎么找到测试程序对应的进程1. pgrep / pidof —— 按进程名pgrep-a程序名# 列出 PID + 完整命令行pgrep-af关键字# -f 匹配完整命令行(推荐)pidof 程序名# 只输出 PID如果启动命令是./my_test --config a.txt,用-f才能匹配到。2. ps + grepps-ef|grepmy_test|grep-vgreppsaux|grepmy_test|grep-vgrepgrep -v grep排除 grep 自身。3. top / htop 里搜top# 运行中按 o,输入 COMMAND=my_test 回车过滤# 或按 u 输入用户名,只看自己的进程htop 里按F4输入名字过滤。4. 只知道端口 / 文件,反查进程ss-lntp|grep端口号lsof-i:端口号ls-l/proc/*/exe2>/dev/null|grepmy_testls-l/proc/*/cwd2>/dev/null|grep/your/workdir5. 找到后看细节ps-fp<PID>cat/proc/<PID>/cmdline|tr'\0'' '# 完整启动命令(含参数)top-p<PID>pidstat-u-p<PID>1常见坑现象原因 / 解决pgrep my_test查不到用pgrep -f my_test匹配完整命令行结果里有 grep 自己加grep -v grep或用pgrep -a进程名被截断(15 字符)用ps -ef看 CMD 列,或cat /proc/PID/cmdline脚本(bash/python)启动进程名是解释器,要用-f按脚本名查多实例pgrep -af全部列出,ps -fp PID1 PID2逐个看三、核心问题:短生命周期程序怎么测才靠谱?程序生命周期短(比如跑几秒就结束),最大的问题是:等你敲完 top/pidstat 命令,程序可能已经结束了。针对这种场景,按推荐程度给出几种方法。方案一:/usr/bin/time -v(最推荐,一步到位)/usr/bin/time-v./your_algorithm输出里直接有这几行,直接读就行:User time (seconds): 8.50 System time (seconds): 0.30 Percent of CPU this job got: 88% Elapsed (wall clock) time: 0:00:10Percent of CPU this job got就是整个进程生命周期内的平均 CPU 占用率,已经帮你算好了。单核程序一般 ≤100%;多线程程序可能 >100%(比如 4 线程跑满显示 400%)。注意要用/usr/bin/time(GNU time),不要用 shell 内置的time,内置的没有-v和 CPU% 字段。如果只想要简单版:time./your_algorithm# 输出 real / user / sys# CPU% = (user + sys) / real × 100方案二:perf stat(硬件级计数,精度高)perfstat./your_algorithm输出关键行:10.012345 task-clock (msec) # 0.998 CPUs utilizedCPUs utilized就是 CPU 占用率(0.998 ≈ 99.8%,多线程可能 >1)。perf stat直接统计 CPU 周期/任务时钟,不依赖时间片采样,对短窗口比读/proc/stat更可靠。如果只想看利用率:perfstat-etask-clock ./your_algorithm方案三:脚本逐秒采样 /proc/PID/stat(想看波动时用)如果你不仅想要平均值,还想知道程序运行期间 CPU 占用是怎么变化的(比如启动峰值、稳态、结束),用脚本启动程序并持续采样:#!/bin/bash./your_algorithm&PID=$!echo"PID=$PID"# 每 0.5 秒采样一次,直到进程结束whilekill-0$PID2>/dev/null;doawk'{print "utime="$14" stime="$15" (jiffies)"}'/proc/$PID/stat2>/dev/nullsleep0.5donewait$PID更实用的版本是直接算百分比(基于/proc/PID/stat的 utime+stime 增量除以时间增量,再除以getconf CLK_TCK):#!/bin/bash./your_algorithm&PID=$!CLK=$(getconf CLK_TCK)prev=0;prev_t=$(date+%s.%N)whilekill-0$PID2>/dev/null;dosleep1read-r_ _ _ _ _ _ _ _ _ _ _ _ _ utime stime _</proc/$PID/stat2>/dev/null||breakcur=$((utime+stime))now=$(date+%s.%N)dt=$(echo"$now-$prev_t"|bc)dcpu=$((cur-prev))pct=$(echo"scale=1;$dcpu* 100 / ($CLK*$dt)"|bc)echo"t=${dt}s CPU=${pct}%"prev=$cur;prev_t=$nowdonewait$PID方案四:pidstat 配合后台启动(可行,但注意时序)pidstat 只能监控已存在的进程,程序跑得快的话,需要先启动再立刻拿到 PID:./your_algorithm&PID=$!pidstat-u-p$PID1wait$PIDpidstat 会每秒输出一行该进程的%CPU,最后你再求平均。缺点是启动到 pidstat 开始采样之间可能有几百毫秒延迟,会漏掉最开始的一小段。如果启动开销不重要,这个方法够用。方案五:top 批处理模式(不推荐用于短程序)./your_algorithm&PID=$!top-b-d1-p$PID能看,但 top 的第一次采样往往不准,短窗口内有效数据点少,不如前面几种方法直接。四、关键注意事项1. 多核 / 多线程:占用率可以 >100%单线程跑满一个核 → 约 100%4 线程跑满 4 个核 → 约 400%/usr/bin/time -v的Percent of CPU和perf stat的CPUs utilized都遵循这个规则,别看到 380% 就以为出错了。2. 单次测量噪声大,建议多跑几次系统后台任务、频率调节、缓存冷热都会影响单次结果。建议至少跑 3~5 次取平均,并观察离散程度:foriin12345;do/usr/bin/time-f"%e s, CPU %P"./your_algorithmdone%P就是Percent of CPU,%e是 wall time。3. 区分“启动开销”和“稳态”如果程序启动阶段有大量初始化(加载模型、分配内存、预热缓存),这部分会拉低平均 CPU 占用率。想测稳态性能,可以在程序内部加一个“预热 + 计时”的机制,只统计核心计算段的 CPU 时间,而不是整个进程生命周期。4. 容器 / 虚拟化环境下的注意点在 Docker 或虚拟机里测 CPU 占用,top显示的可能是宿主机的整体负载,而不是容器实际分配到的配额。此时建议:用docker stats看容器 CPU 使用用cat /sys/fs/cgroup/cpu/cpuacct.usage读容器累计 CPU 时间注意st(steal time)列,它表示被宿主机偷走的时间,虚拟化环境下可能偏高5. 别忽略 IO 等待如果程序大量读写磁盘,CPU 占用率看起来不高,但实际运行时间很长。此时wa列会偏高,说明瓶颈在 IO 而不是 CPU。测性能时要结合iostat一起看,避免误判。五、总结场景推荐工具日常快速查看top/htop看多核分布mpstat -P ALL 1找进程pgrep -af/ps aux --sort=-%cpu短程序平均占用/usr/bin/time -v(最推荐)高精度计数perf stat看波动曲线脚本采样/proc/PID/stat历史趋势sar -u核心结论:短生命周期程序,直接用/usr/bin/time -v或perf stat,一步到位拿到平均 CPU 占用率;想看波动再上脚本采样。别再用 top 去追一个已经跑完的程序了。
前言最近做算法性能测试时,遇到一个很典型的问题:测试程序跑起来后,怎么知道它占了多少 CPU?用 top 看总感觉不够精确,有没有更靠谱的办法?这个问题看似简单,实则踩了不少坑。网上大部分教程都在讲 top、pidstat 怎么看 CPU 占用,但不同场景下该用哪个工具、结果怎么解读、有哪些坑,往往语焉不详。这篇文章把整个排查过程整理出来,包括:Linux 查看 CPU 占用率的常用方法及适用场景怎么找到测试程序对应的进程短生命周期程序怎么测才靠谱(核心)结果解读与常见坑一、Linux 查看 CPU 占用率的常用方法先快速过一遍基础工具,按场景选:场景推荐命令日常排查top/htop看多核负载是否均衡mpstat -P ALL 1找吃 CPU 的进程ps aux --sort=-%cpu | head或pidstat -u 1历史/趋势分析sar -u写脚本取数值读/proc/stat或解析mpstat1. top —— 最常用top第一行%Cpu(s)显示整体占用:us:用户态sy:内核态id:空闲率(CPU 占用率 ≈ 100 - id)wa:IO 等待st:被虚拟化偷走的时间按1展开每个核心,按P按 CPU 排序,按q退出。2. mpstat —— 看每个核心mpstat-PALL1# 每 1 秒刷新,显示所有核心需安装sysstat。%idle是空闲率,100 - %idle即该核心占用率。3. vmstat —— 整体概况vmstat15# 每秒采样,共 5 次关注us、sy、id、wa列。4. ps —— 查某进程psaux--sort=-%cpu|head-10# 按 CPU 占用降序取前 10ps-p<PID>-o%cpu,cmd# 查看指定进程5. pidstat —— 按进程持续统计pidstat-u15# 每秒采样,共 5 次pidstat-u-p<PID>1# 只看某个进程6. sar —— 历史数据sar-u15# 实时sar-u-f/var/log/sa/sa15# 某天的历史数据7. 脚本里直接取数值读/proc/stat两次采样求差值:readcpu a b c idle rest</proc/statt1=$((a+b+c+idle));i1=$idlesleep1readcpu a b c idle rest</proc/statt2=$((a+b+c+idle));i2=$idleecho"CPU 占用率:$((100-(i2-i1)*100/(t2-t1)))%"二、怎么找到测试程序对应的进程1. pgrep / pidof —— 按进程名pgrep-a程序名# 列出 PID + 完整命令行pgrep-af关键字# -f 匹配完整命令行(推荐)pidof 程序名# 只输出 PID如果启动命令是./my_test --config a.txt,用-f才能匹配到。2. ps + grepps-ef|grepmy_test|grep-vgreppsaux|grepmy_test|grep-vgrepgrep -v grep排除 grep 自身。3. top / htop 里搜top# 运行中按 o,输入 COMMAND=my_test 回车过滤# 或按 u 输入用户名,只看自己的进程htop 里按F4输入名字过滤。4. 只知道端口 / 文件,反查进程ss-lntp|grep端口号lsof-i:端口号ls-l/proc/*/exe2>/dev/null|grepmy_testls-l/proc/*/cwd2>/dev/null|grep/your/workdir5. 找到后看细节ps-fp<PID>cat/proc/<PID>/cmdline|tr'\0'' '# 完整启动命令(含参数)top-p<PID>pidstat-u-p<PID>1常见坑现象原因 / 解决pgrep my_test查不到用pgrep -f my_test匹配完整命令行结果里有 grep 自己加grep -v grep或用pgrep -a进程名被截断(15 字符)用ps -ef看 CMD 列,或cat /proc/PID/cmdline脚本(bash/python)启动进程名是解释器,要用-f按脚本名查多实例pgrep -af全部列出,ps -fp PID1 PID2逐个看三、核心问题:短生命周期程序怎么测才靠谱?程序生命周期短(比如跑几秒就结束),最大的问题是:等你敲完 top/pidstat 命令,程序可能已经结束了。针对这种场景,按推荐程度给出几种方法。方案一:/usr/bin/time -v(最推荐,一步到位)/usr/bin/time-v./your_algorithm输出里直接有这几行,直接读就行:User time (seconds): 8.50 System time (seconds): 0.30 Percent of CPU this job got: 88% Elapsed (wall clock) time: 0:00:10Percent of CPU this job got就是整个进程生命周期内的平均 CPU 占用率,已经帮你算好了。单核程序一般 ≤100%;多线程程序可能 >100%(比如 4 线程跑满显示 400%)。注意要用/usr/bin/time(GNU time),不要用 shell 内置的time,内置的没有-v和 CPU% 字段。如果只想要简单版:time./your_algorithm# 输出 real / user / sys# CPU% = (user + sys) / real × 100方案二:perf stat(硬件级计数,精度高)perfstat./your_algorithm输出关键行:10.012345 task-clock (msec) # 0.998 CPUs utilizedCPUs utilized就是 CPU 占用率(0.998 ≈ 99.8%,多线程可能 >1)。perf stat直接统计 CPU 周期/任务时钟,不依赖时间片采样,对短窗口比读/proc/stat更可靠。如果只想看利用率:perfstat-etask-clock ./your_algorithm方案三:脚本逐秒采样 /proc/PID/stat(想看波动时用)如果你不仅想要平均值,还想知道程序运行期间 CPU 占用是怎么变化的(比如启动峰值、稳态、结束),用脚本启动程序并持续采样:#!/bin/bash./your_algorithm&PID=$!echo"PID=$PID"# 每 0.5 秒采样一次,直到进程结束whilekill-0$PID2>/dev/null;doawk'{print "utime="$14" stime="$15" (jiffies)"}'/proc/$PID/stat2>/dev/nullsleep0.5donewait$PID更实用的版本是直接算百分比(基于/proc/PID/stat的 utime+stime 增量除以时间增量,再除以getconf CLK_TCK):#!/bin/bash./your_algorithm&PID=$!CLK=$(getconf CLK_TCK)prev=0;prev_t=$(date+%s.%N)whilekill-0$PID2>/dev/null;dosleep1read-r_ _ _ _ _ _ _ _ _ _ _ _ _ utime stime _</proc/$PID/stat2>/dev/null||breakcur=$((utime+stime))now=$(date+%s.%N)dt=$(echo"$now-$prev_t"|bc)dcpu=$((cur-prev))pct=$(echo"scale=1;$dcpu* 100 / ($CLK*$dt)"|bc)echo"t=${dt}s CPU=${pct}%"prev=$cur;prev_t=$nowdonewait$PID方案四:pidstat 配合后台启动(可行,但注意时序)pidstat 只能监控已存在的进程,程序跑得快的话,需要先启动再立刻拿到 PID:./your_algorithm&PID=$!pidstat-u-p$PID1wait$PIDpidstat 会每秒输出一行该进程的%CPU,最后你再求平均。缺点是启动到 pidstat 开始采样之间可能有几百毫秒延迟,会漏掉最开始的一小段。如果启动开销不重要,这个方法够用。方案五:top 批处理模式(不推荐用于短程序)./your_algorithm&PID=$!top-b-d1-p$PID能看,但 top 的第一次采样往往不准,短窗口内有效数据点少,不如前面几种方法直接。四、关键注意事项1. 多核 / 多线程:占用率可以 >100%单线程跑满一个核 → 约 100%4 线程跑满 4 个核 → 约 400%/usr/bin/time -v的Percent of CPU和perf stat的CPUs utilized都遵循这个规则,别看到 380% 就以为出错了。2. 单次测量噪声大,建议多跑几次系统后台任务、频率调节、缓存冷热都会影响单次结果。建议至少跑 3~5 次取平均,并观察离散程度:foriin12345;do/usr/bin/time-f"%e s, CPU %P"./your_algorithmdone%P就是Percent of CPU,%e是 wall time。3. 区分“启动开销”和“稳态”如果程序启动阶段有大量初始化(加载模型、分配内存、预热缓存),这部分会拉低平均 CPU 占用率。想测稳态性能,可以在程序内部加一个“预热 + 计时”的机制,只统计核心计算段的 CPU 时间,而不是整个进程生命周期。4. 容器 / 虚拟化环境下的注意点在 Docker 或虚拟机里测 CPU 占用,top显示的可能是宿主机的整体负载,而不是容器实际分配到的配额。此时建议:用docker stats看容器 CPU 使用用cat /sys/fs/cgroup/cpu/cpuacct.usage读容器累计 CPU 时间注意st(steal time)列,它表示被宿主机偷走的时间,虚拟化环境下可能偏高5. 别忽略 IO 等待如果程序大量读写磁盘,CPU 占用率看起来不高,但实际运行时间很长。此时wa列会偏高,说明瓶颈在 IO 而不是 CPU。测性能时要结合iostat一起看,避免误判。五、总结场景推荐工具日常快速查看top/htop看多核分布mpstat -P ALL 1找进程pgrep -af/ps aux --sort=-%cpu短程序平均占用/usr/bin/time -v(最推荐)高精度计数perf stat看波动曲线脚本采样/proc/PID/stat历史趋势sar -u核心结论:短生命周期程序,直接用/usr/bin/time -v或perf stat,一步到位拿到平均 CPU 占用率;想看波动再上脚本采样。别再用 top 去追一个已经跑完的程序了。