第 8 章 性能观测工具链¶
调优没有测量就是玄学。观测工具链按"看什么"分四层:内核视图(procfs / sysfs,最短入口)、统计快照(vmstat / iostat / mpstat / pidstat / htop)、采样与追踪(perf 火焰图、ftrace 事件、bpftrace 一行式)、面板与远程(Netdata / Grafana + InfluxDB 时序面板、GPU 侧 nvidia-smi / nvtop / intel_gpu_top)。
AI 部署链另有一套观测对象(PyTorch + CUDA 三渠道安装的版本对应,见 3.3 节):装完不等于跑得对,跑对了要看 GPU 利用率与显存曲线。末节给出"查一个抖动该开哪几样"的组合经验。
1. procfs 与 sysfs¶
第 4 章「进程、服务与设备」已把 /proc/<pid>/ 与 /sys/devices/ 定义为"内核视图",本章给出读数集合:
## 系统与内存
cat /proc/meminfo # 内存与 swap 总量、Slab、Buffers/Cached
cat /proc/loadavg # 1 / 5 / 15 分钟负载与最近 PID
cat /proc/uptime # 主机运行秒数
cat /proc/cpuinfo | head # 每核型号与频率
## 单进程
cat /proc/<pid>/status | grep -E 'VmRSS|VmLck|State|Threads'
cat /proc/<pid>/stat # 26 列,含 utime / stime / prio / policy
ls -l /proc/<pid>/fd # 打开的文件描述符
cat /proc/<pid>/io # read_bytes / write_bytes / cancelled_write_bytes
sudo cat /proc/<pid>/smaps_rollup # 内存明细汇总(Pss、Rss、Locked)
cat /proc/<pid>/cgroup # 归属的 cgroups v2 路径
## 中断、软中断、调度
cat /proc/interrupts # 每 IRQ 每核的计数
cat /proc/softirqs # 每 CPU 软中断计数
cat /proc/schedstat # 每核的等待 / 运行时间
ls /sys/kernel/debug/tracing/ # ftrace 挂载点(属第 4 节)
判据:读 /proc 与 /sys 是零成本的观测手段,脚本与 systemd 定时器都能拿来落 CSV;所有更高级的工具(htop、collectd、node_exporter)都在这些文件之上做整理与滚动窗口。
按"看什么"分的四层观测工具:
flowchart TD
l1["第一层:内核视图<br/>/proc · /sys"] --> l2["第二层:统计快照<br/>vmstat · iostat · pidstat"]
l2 --> l3["第三层:采样与追踪<br/>perf · ftrace · bpftrace"]
l3 --> l4["第四层:面板与远程<br/>Grafana + InfluxDB · nvidia-smi"]
2. 统计快照与交互观测¶
坑:vmstat 首行是开机以来均值
不带参数或带间隔时,第一行统计的是从开机到现在的平均;判断当前负载要从第二行起看。
2.1 sysstat 家族¶
Ubuntu 需要显式装 sysstat 包(服务器安装常自带,桌面安装可能缺)。核心四个:
vmstat 1 5 # 每秒 5 次;r=运行队列、b=阻塞、si/so=换页、us/sy/wa=CPU 分配
iostat -xz 1 # 每块设备的 %util、await、r/s、w/s、rMB/s、wMB/s
mpstat -P ALL 1 # 每 CPU 的 %usr/%sys/%irq/%soft/%idle
pidstat 1 # 每 PID 的 CPU 与 IO;-d 只看 IO、-r 只看内存、-w 只看上下文切换
判据(Brendan Gregg 系统性能军规):
- vmstat 的 r 长期大于核数说明有排队;wa 大于 20% 说明磁盘是瓶颈。
- iostat 的 %util 对 SSD 接近 100 不代表饱和(SSD 并发高),但机械盘 100 就是极限;await 显著大于服务时间均值说明有排队。
- mpstat 单核 100% 而整机不高——往往是绑核的实时线程或单线程处理瓶颈。
- pidstat -w 看 cswch/s(自愿切换,多为等 IO 或 sleep)与 nvcswch/s(非自愿切换,被抢占)——非自愿高说明被同优先级抢,是调度问题;自愿高说明被 IO 拖。
定位一个人为制造的热点:一个 Python 单线程纯计算循环,pidstat -p <pid> 1 3 逐秒报 %usr 稳定 99–100%、%CPU 100、CPU 列固定在同一核——单线程进程只吃一个核,不自动扩到多核;同时刻 mpstat -P ALL 里那一核拉满、其余空闲。进程级归因免 root 即可,函数级热点才需下面的 perf。
2.2 top / htop / btop¶
top:任何机器都有,batch模式(top -b -n1)适合脚本采集。htop:交互友好;F2 设置列、F4 过滤、F5 树视图;显示 CPU 分配条与内存条更直观。btop:现代 TUI,鼠标可点、主题多;对高刷屏与触屏友好。
三者背后都是 /proc/stat 与 /proc/<pid>/stat;差异是界面与默认展开。判据:机器人上位机 SSH 现场,htop 是最低可用集;脚本采集走 pidstat 或 ps 更方便(无 curses)。
3. GPU 观测与 AI 部署链¶
3.1 NVIDIA 侧¶
nvidia-smi # 一次性快照:驱动版本、显存、利用率、每进程
nvidia-smi -l 1 # 每秒刷新(旧式)
nvidia-smi dmon -s um -c 10 # 只输出利用率与显存,10 次
nvidia-smi pmon -s um # 每进程粒度:sm / mem / enc / util
watch -n1 nvidia-smi
关键字段:
- Volatile GPU-Util(%):过去 1 秒内有 kernel 在执行的采样比例,不代表吞吐。100% 也可能只跑了大卡的极小负载。
- Memory-Usage:显存占用;OOM 前必爆。
- Temp / Power/Performance:降频的直接原因。
- Processes:GPU 上下文持有者;僵尸 CUDA 进程会霸占显存直到 pid 死。
nvtop(多卡与历史曲线)比 nvidia-smi 更适合长跑观测:sudo apt install nvtop。
3.2 非 NVIDIA 侧¶
- Intel 集显与 Arc 独显:
intel_gpu_top(sudo apt install intel-gpu-tools),按引擎(Render / Copy / Video)分列利用率。 - AMD:
radeontop或新版amdgpu_top(后者常需第三方仓库)。两者读数口径不同——radeontop采样 X 侧与驱动暴露的占用,amdgpu_top直接读gpu_metrics与 fdinfo,不必互相对照;非 NVIDIA 侧的具体解读此处不展开。
nvidia-smi 与 htop 语义不同:前者看 GPU 侧计算单元与显存、后者看 CPU 与主存;把两个窗口并排放是最常见的工作状态,读数不能互相推断。
3.3 AI 部署链:三渠道版本对应¶
PyTorch + CUDA 从安装到确认可用的完整链,接第 3 章「编辑器、包管理与开发环境」的 Python 三生态:
| 渠道 | 命令 | 附带什么 |
|---|---|---|
| conda-forge / pytorch | mamba install pytorch torchvision pytorch-cuda=12.1 -c pytorch -c nvidia |
conda 环境内的 Python + PyTorch + cudnn/cublas 与 CUDA runtime;驱动本身仍是宿主 |
| pip + PyTorch | pip install torch --index-url https://download.pytorch.org/whl/cu121 |
同上,走 venv 更常见;wheel 里打包 CUDA runtime |
| apt | sudo apt install nvidia-cuda-toolkit |
完整 CUDA Toolkit + nvcc;与 PyTorch wheel 里的 runtime 不必同版本 |
判据:驱动版本 ≥ PyTorch wheel 需要的最高 CUDA runtime,反之则不成立;nvidia-smi 报的 CUDA Version 是"驱动支持的最高 runtime",不是"当前 Python 里加载的"。查后者:
python -c 'import torch; print(torch.__version__, torch.version.cuda, torch.cuda.get_device_capability())'
渠道形态本身有文档依据:PyTorch 发布页给出 pip wheel 按 CUDA runtime 分档的 index(download.pytorch.org/whl/cu118 / cu121 / cu124 / cu126 等,同一 torch 版本常有多档可选),wheel 与 conda 自带 runtime;版本对应因此可以只查表,不必逐个试装。
NVIDIA CUDA Installation Guide 的 Ubuntu / Debian 路径为 cuda-keyring_1.1-1_all.deb 网络仓库(dpkg -i 后走 apt,发行代号 ubuntu2204 / ubuntu2404 / ubuntu2604,amd64 与 arm64 均有),装的是完整 Toolkit。
nvcc 只在编译 CUDA 源码(自定义算子、第三方库源码构建)时才需要,纯推理不必与 wheel 内的 runtime 同版本。三条渠道的差异落在分层:wheel 与 conda 自带 runtime、只要求驱动够新;apt 装完整 Toolkit、版本由安装的包决定,彼此不共享。
4. 内核级追踪¶
4.1 perf:采样与热点¶
perf 用 CPU 的 PMU(Performance Monitoring Unit)做采样式观测,开销可控:
## 全局采样 30 秒
sudo perf record -a -g -- sleep 30
sudo perf report
## 单进程采样 10 秒
sudo perf record -F 99 -p <pid> -g -- sleep 10
sudo perf report --stdio
## 快速看数:上下文切换、page fault、CPU 迁移
sudo perf stat -a -- sleep 10
非 root 能不能跑 perf 取决于 perf_event_paranoid(上面均带 sudo):这个值在 Ubuntu 上常为 4,手册页标尺里 >= 1 即“Disallow CPU event access”——非 root 下 perf record / perf stat -e cycles 直接失败(产出空数据或 permission 报错),故进程级热点先用免 root 的 pidstat,要函数级火焰图再走下面三条任一:临时 sudo sysctl kernel.perf_event_paranoid=1(或 -1)、给二进制 CAP_PERFMON(sudo setcap cap_perfmon+ep $(command -v perf))、或直接 sudo perf。
火焰图:把 perf script 输出喂给 FlameGraph 脚本生成:
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
两个 Perl 脚本不在 Ubuntu 主仓库里(apt-cache search flamegraph 仅命中 austin,一个 Python 采栈工具),需从上游 FlameGraph 仓库取。
打开 svg 就能一眼看出"哪个符号占了大半个横条"。判据:火焰图横轴是采样覆盖广度不是时间序列,宽就是热点;竖着是调用链。
4.2 ftrace 与 trace-cmd¶
ftrace 是内核自带的追踪框架,挂载在 /sys/kernel/debug/tracing/;trace-cmd 是它的命令行前端,能落文件回放:
## 抓 5 秒的调度事件
sudo trace-cmd record -e sched_switch -e sched_wakeup -F sleep 5
sudo trace-cmd report | head
sudo trace-cmd extract # 生成 .dat,可用 kernelshark 打开
## 直读 tracepoint(不用 trace-cmd)
sudo sh -c 'echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable'
sudo cat /sys/kernel/debug/tracing/trace_pipe
sudo sh -c 'echo 0 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable'
常用事件族:sched/(调度、迁移)、irq/(中断进入 / 退出)、block/(IO 请求)、syscalls/(系统调用)、preemptirq/(关抢占窗口)。
4.3 eBPF 与 bpftrace:一行式探测¶
eBPF(extended Berkeley Packet Filter)让内核执行用户写的沙箱化字节码,比 ftrace 更灵活。bpftrace 是其一行式脚本前端(awk-like)。eBPF 的探针依赖内核 BTF:/sys/kernel/btf/vmlinux 存在即具备前置(22.04 的 5.15 与 6.8 HWE 内核均带),bpftrace 在 22.04 仓库的版本是 0.14.0。
## 谁在调 openat,按文件名汇总
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat /comm=="ros2"/ { @[str(args->filename)] = count(); }'
## 每进程 1 秒内的调度切换数
sudo bpftrace -e 'tracepoint:sched:sched_switch { @[comm] = count(); } interval:s:1 { exit(); }'
## 阻塞超过 100 ms 的块 IO 请求
sudo bpftrace -e 'tracepoint:block:block_rq_issue { @start[tid] = nsecs; }
tracepoint:block:block_rq_complete /@start[tid] && (nsecs - @start[tid]) > 100000000/ {
printf("slow io: %d ms\n", (nsecs - @start[tid])/1000000);
delete(@start[tid]);
}'
判据:bpftrace 依赖内核的 BTF(BPF Type Format),ls /sys/kernel/btf/vmlinux 存在才说明当前内核带类型信息;Ubuntu 22.04(5.15)与 24.04(6.8)的内核都启用了 BTF,差异主要在工具版本(22.04 的 bpftrace 0.14、24.04 的 0.20),新版本内置函数与语法更多,迁移旧脚本时以 bpftrace -l 能列出的探针为准。BPF 工具家族(BCC、bpftop、biolatency 等)与 ROS 2 无关,本板块只用 bpftrace 一行式。
5. 面板与"查一次抖动该开哪几样"¶
5.1 时序面板¶
- Netdata:单机 Web 面板,装完即出图;
sudo apt install netdata,主仓库未收录的发行版用上游kickstart.sh安装。 - Grafana + InfluxDB / VictoriaMetrics + collectd / node_exporter:跨机、长期存储、告警;机器人上位机常与 ROS 2 计算图监控并排挂。
- Prometheus + node_exporter:云原生栈,机器人集群运维场景常用。
判据:面板适合"看趋势与告警",现场排"刚才那一次抖"仍要靠 perf / trace-cmd 抓现场。面板数据粒度与保留时长都要设计——超过两周的秒级数据不留在本地。
5.2 数据侧最小分析¶
面板之外,落 CSV 用 pandas 是最短分析路径:
## 把 vmstat 1 的样本落 CSV 后画一次分位
import pandas as pd
df = pd.read_csv("vmstat.csv", sep=r"\s+", skipinitialspace=True)
print(df["wa"].describe(percentiles=[.5, .95, .99]))
df.plot(y=["us", "sy", "wa"], title="CPU 分配")
mcap / rosbag 与轨迹工具归 ROS2-note;本板块止于"把系统层数字落到 CSV 与图"。
5.3 组合拳:一次抖动的最短路径¶
机器人现场报"控制回路偶发丢 2 ms deadline",最短查法:
- 先量:
cyclictest(第 7 章)跑一次基线,把 Max 与 99.9 分位记下来,作为后面每次改动后复测的对照。 - 看调度:
sudo trace-cmd record -e sched:* -e irq:* -F <controller pid>抓一段包含抖动的窗口,kernelshark打开看是不是被打断、被谁打断。 - 看中断:
cat /proc/interrupts差值找出该时段增量最大的 IRQ;
/proc/irq/<n>/smp_affinity 看落在哪个核。
4. 看内存:pidstat -d -p <pid> 1 与 perf stat -e page-faults -p <pid>;缺页或 IO 引起的抖动这条最直白。
5. 看频率与 C 态:cpupower info -f、cat /sys/devices/system/cpu/cpu*/cpuidle/state*/usage 差值。
6. 看网络:ss -ti、ip -s link、必要时 tcpdump。
判据:每一步都要留下"读数 + 命令 + 环境"三件套;调优一次改一项、复测一次;一次性上十个改动等于放弃归因。
判据:一次抖动的最短排查路径
先看 /proc 与 vmstat 排除内存与 IO,再用 perf/ftrace 定位调度与中断,最后才上 cyclictest 量化——顺序反了会把时间花在复现环境上。
6. 要点¶
- 观测分四层:
/proc与/sys是最短入口;sysstat家族与htop给统计快照;perf/ftrace/bpftrace到内核级;面板做长期趋势与告警。 - GPU 观测 CPU 观测不能互推:
nvidia-smi报的是 GPU 利用率与显存占用,htop报的是 CPU 与主存;机器人推理链上两者都要看。 - AI 部署链三渠道各有版本配对:conda / pip / apt 装的 PyTorch + CUDA 与驱动的支持面不同;驱动版本 ≥ wheel 需要的最高 CUDA runtime 是硬约束。
- perf / ftrace / bpftrace 各管一段:采样热点用 perf、事件序列用 ftrace、可编程观测点用 bpftrace;BTF 支持随内核版本,24.04 优于 22.04。
- 一次抖动 = 六步组合拳:cyclictest → trace-cmd →
/proc/interrupts→ pidstat → cpupower → ss;每项读数 + 命令 + 环境三件套一起留。 - 面板不替代现场抓取:Grafana 告诉你"上周日晚有 12 次尖峰",
perf record才能告诉你"那一次到底是哪个函数吃掉了 500 μs"。