跳转至

第 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",最短查法:

  1. 先量:cyclictest(第 7 章)跑一次基线,把 Max 与 99.9 分位记下来,作为后面每次改动后复测的对照。
  2. 看调度:sudo trace-cmd record -e sched:* -e irq:* -F <controller pid> 抓一段包含抖动的窗口,kernelshark 打开看是不是被打断、被谁打断。
  3. 看中断: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"。

参考文献与延伸阅读

  • perf. Wiki 与文档. 文档
  • Linux Kernel. 性能与追踪文档. 文档
  • Grafana. 文档. 文档