这一章在做什么

回答两个问题:CPU 是不是瓶颈,如果是,时间花在了哪段代码上。先分清「CPU 很忙」(利用率)和「CPU 不够用、线程在排队」(饱和度),再从全系统看到每个核、每个进程和线程,最后用采样剖析把 CPU 时间落到函数上,画成火焰图。顺带讲清负载平均在 Linux 上为什么不只代表 CPU,以及忙不等于在干活:周期可能都耗在等内存上。

一个线程在 10 秒里的时间去哪了 在 CPU 上:用户态 内核态 排队等 CPU 睡着:等 IO、等锁 利用率:mpstat、pidstat 的 %usr / %sys,剖析器的样本 饱和度:runqlat、pidstat 的 %wait off-CPU:不归这一章,见第 5 章 比例是示意。CPU 剖析只采到左边两段;线程睡着时,再怎么采样也看不见它。
本站画的示意。这一章管左边三段:在 CPU 上花了多少、花在哪,以及想上 CPU 却要排队的时间。

什么时候翻这一章

这一章的方法来自第 2 章(USE 方法、负载特征),剖析和火焰图第 5 章讲过应用这一侧,perf 和 BPF 工具的细节在第 13、15 章。线程不在 CPU 上的时间(等锁、等 IO)归第 5 章的 off-CPU 分析。

先用中文过一遍

利用率和饱和度:忙和不够是两回事

利用率(utilization)问的是:一段时间里,这个 CPU 有多少时间不在跑空闲线程。80% 就是十秒里有八秒在干活。饱和度(saturation)问的是:有多少线程已经准备好了,却因为 CPU 全被占着而在排队。两者的关系像收银台:收银员一直在忙是利用率,后面排着的队是饱和度。

CPU 和磁盘不同的地方是,到了 100% 也不一定出事:内核有优先级和抢占,高优先级的工作可以插队(§6.3.10)。所以看 CPU 不能只看利用率,要同时看排队:Linux 上是 vmstat 的 r 列(正在跑的加上排队的)、pidstat 的 %wait,以及 BCC 的 runqlat 量出的每次排队等了多久。还有一点:利用率要按核看。8 核机器平均 12%,可能是一个核 100%、其余全闲,这正是单线程瓶颈的样子(§6.6.3)。

4 个 CPU 全忙,还有 3 个线程在等 CPU 0线程 A CPU 1线程 B CPU 2线程 C CPU 3线程 D E F G 利用率 100%(四个核都没在跑空闲线程) 饱和度:3 个在排队 它们等的时间就是调度延迟 vmstat 的 r = 7(4 个在跑 + 3 个在等);r 长期大于核数,说明 CPU 不够用
本站画的示意。真实的调度器给每个 CPU 一个队列,Linux 的 CFS 用的是红黑树而不是队列,但「在等 CPU」这个概念不变(§6.2.3)。

负载平均:在 Linux 上不只是 CPU

负载平均(load average)是一个数:正在用资源的加上在排队的,做成指数衰减的移动平均。uptime 给出三个,分别是 1、5、15 分钟的。三个数一比,就知道负载在涨还是在落:如果 1 分钟的远小于 15 分钟的,问题可能已经过去了。

容易误读的地方有两个。第一,在 Linux 上它不只算 CPU:处于不可中断睡眠(D 状态,多半在等磁盘)的线程也算进去,所以负载 8 的机器可能 CPU 很闲、磁盘很忙。第二,「1 分钟平均」不是过去一分钟的算术平均:一个持续占满一个核的线程跑了一分钟,1 分钟的值大约才到 0.6(§6.6.1)。书里的建议是:负载平均只用来起头,别在上面花超过五分钟。要按资源分开看,用 PSI(压力停顿信息):/proc/pressure/cpu 告诉你有任务因为等 CPU 而停顿的时间比例。

Linux 的负载平均把什么加在一起 正在 CPU 上跑R 状态 + 在排队等 CPUR 状态 + 不可中断睡眠D 状态,多半在等磁盘 负载平均:三项相加,再做 1、5、15 分钟的指数衰减平均 PSI 的 cpu:只看「等 CPU」停顿的时间比例
本站画的示意。负载 8 在 4 核机器上可能是 CPU 不够,也可能是一堆线程卡在 IO 上;PSI 和 vmstat 的 r、b 两列能把它们分开。

剖析:采样,再叠成火焰图

想知道 CPU 时间花在哪,最省事的办法是定时采样:每秒在每个 CPU 上打断 99 次,记下当时正在跑的调用栈(§6.5.4)。用 99 而不是 100,是因为 100 Hz 可能和某些周期性活动步调一致,让样本系统性地偏向它(书里的说法是 may)。采到几千个栈之后,一条一条读不过来,就把相同的栈合并计数,画成火焰图(flame graph):每一格是一个函数,下面一格是它的调用者;格子越宽,说明它(连同它调用的函数)在越多的样本里出现。

读火焰图的规矩只有几条:横轴不是时间,从左到右按函数名排序,左右位置没有意义;顶上一层是采样那一刻正在 CPU 上的函数;先看最宽的「塔」,再看顶上又宽又平的一段(平台,plateau),那是直接烧 CPU 的地方。采样只看见在 CPU 上的时间:线程在睡、在等锁、在等 IO 时不会被采到,所以火焰图里看不到「慢在等待」的问题。

四个样本 → 一张火焰图 采到的栈(栈顶在上) parse handle main comp handle main comp handle main tick main 合并后的火焰图(宽度 = 样本数) parse comp handle tick main comp:2/4 = 50%,在顶上,直接烧 CPU handle:3/4,不在顶上,时间在子函数里 左右顺序按函数名排,不是时间先后 线程睡着时(等 IO、等锁) 不会被采到,火焰图里没有它
本站画的示意,函数名是编的。真实的剖析每秒每核采 99 个样本,几十秒就是几万个栈,合并的道理一样。下面的模拟器可以一步一步看这个过程。

IPC:忙不等于在干活

利用率把一个 CPU 周期算作「忙」,不管这个周期是在执行指令,还是在干等内存把数据送上来。IPC(每周期指令数)把这两者分开:perf 用硬件计数器数一段时间里执行了多少条指令、过了多少个周期,一除就是 IPC。书里举的例子是:低 IPC 的负载在 0.2 上下或更低,高的能超过 1,但具体数值看处理器,最好用已知负载自己量一量(§6.3.7)。IPC 低,说明 CPU 多半在等内存,该想的是数据布局、缓存命中,换主频更高的 CPU 帮助不大;IPC 高,说明指令跑得顺,该想的是能不能少执行一些指令。注意 IPC 高不代表代码高效:一个只在寄存器里空转的循环,IPC 很高,干的却是无用功。

英文中文一句话
utilization利用率一段时间里 CPU 不在跑空闲线程的比例,要按核看 §6.3.8 术语页
saturation饱和度想上 CPU 却在排队的程度;100% 利用率之后才有 §6.3.10 术语页
run queue运行队列可以跑、在等 CPU 的线程;Linux 实际用红黑树,叫法沿用下来 §6.2.3
scheduler latency调度延迟线程从可以跑到真正跑上 CPU 等了多久,也叫运行队列延迟 §6.6.16 术语页
load average负载平均在用加在等的数量的指数衰减平均;Linux 上含 D 状态 §6.6.1 术语页
PSI (pressure stall information)压力停顿信息按 CPU、内存、IO 分开给出任务停顿的时间比例,Linux 4.20 加入 §6.6.1 术语页
user time / kernel time用户态时间 / 内核态时间两者的比例说明负载的类型:计算型几乎全是用户态 §6.3.9
IPC (instructions per cycle)每周期指令数区分「忙着执行」和「忙着等内存」,倒数是 CPI §6.3.7 术语页
PMC (performance monitoring counter)硬件性能计数器处理器里能编程计数的寄存器:周期、指令、缓存未命中 §6.4.1 术语页
profiling剖析定时采样调用栈,统计 CPU 时间花在哪些代码路径上 §6.5.4 术语页
flame graph火焰图把采到的栈合并画出来,宽度是样本数,横轴不是时间 §6.7.3 术语页
hot CPU热核别的核闲着、它一个跑满,多半是单线程或中断集中 §6.6.3
steal time被偷走的时间虚拟机里,宿主把这个虚拟 CPU 的时间给了别的租户 §6.6.2

方法:按什么顺序查

书里列了十一种 CPU 的方法,作者自己推荐的顺序是:性能监控、USE 方法、剖析、微基准测试、静态调优(§6.5)。落到一次排查上,本站整理成下面几步。前四步几分钟就能做完,用的都是 sysstat 和 procps 里的老工具;第五步开始要 perf 或 BPF。

  1. 负载在涨还是在落?

    uptime 看三个负载平均,cat /proc/pressure/cpu 看 CPU 停顿比例。在落,说明问题可能已经过去,别在这一步停留。§6.6.1

  2. CPU 忙吗?每个核都忙吗?

    mpstat -P ALL 1:看每个核的 %usr + %sys。全都接近 100% 是整体不够;一个核满、其余闲是单线程或中断集中;虚拟机里顺便看 %steal。§6.6.3

  3. 有没有在排队?

    vmstat 1 的 r 长期大于核数,或 pidstat 的 %wait 明显不为零,就是饱和了。要量每次等多久,用 runqlat(BCC,要 root)。容器里还要按限额算。§6.5.2§6.6.16

  4. 谁在用?用户态还是内核态?

    pidstat 1 找出进程,pidstat -t -p PID 1 看到线程。%system 高说明时间花在系统调用或中断上,往第 5 章的系统调用分析走。§6.6.7

  5. 为什么在用:哪段代码?

    perf record -F 99 -g 采样,perf report 看文本,或者画成火焰图。先看最宽的塔,再看顶上的平台。§6.5.4§6.7.3

  6. 周期是在执行指令还是在等内存?

    perf stat 看 IPC。低(比如 0.3 以下)往缓存和内存访问上查;高就去减少指令。§6.5.5

  7. 别漏掉的:中断和配置

    mpstat 的 %irq、%soft,/proc/softirqs;频率调节器是不是 powersave;有没有 cgroup 限额。这些在剖析里不显眼,却常常是答案。§6.5.7§6.6.18

工具速查

想知道敲什么看哪里节
负载的趋势uptime
cat /proc/pressure/cpu
1、5、15 分钟三个数的走向;PSI 的 some avg10 是最近 10 秒有任务在等 CPU 的时间比例§6.6.1
全局利用率和排队vmstat 1r 和核数比;us + sy;wa 是 CPU 闲着而有线程在等磁盘;st 是被宿主拿走的§6.6.2
每个核mpstat -P ALL 1有没有单个核 %usr + %sys 接近 100%;%soft 集中在某个核,常常是网卡中断没摊开§6.6.3
哪个进程、线程pidstat 1
pidstat -t -p PID 1
%usr、%system 分开看;%wait 是等 CPU 的时间§6.6.7
交互地看top,按 1 看每核,H 看线程%CPU 默认不除以核数(top 叫它 Irix 模式,按 I 切换),两个线程跑满就是 200%§6.6.6
哪段代码在烧 CPUperf record -F 99 -g -p PID -- sleep 30
perf report --stdio
按样本占比排的函数和调用路径;看到一串十六进制地址是缺符号§6.6.13
画火焰图perf script > out.perf,再用 FlameGraph 的两个脚本转成 SVG最宽的塔、顶上的平台;在浏览器里点击放大、Ctrl-F 搜索§6.7.3
低开销剖析(BPF)profile-bpfcc -F 49 30栈在内核里汇总,只交出计数,比 perf 写文件省;-f 输出能直接画火焰图§6.6.14
每次排队等了多久runqlat-bpfcc 10 1直方图里毫秒级的桶有没有数;开销比 runqlen 大§6.6.16
队列有多长(便宜)runqlen-bpfcc 10 1队列长度为 0 的比例;按 99 Hz 采样,开销很小§6.6.17
IPCperf stat -e cycles,instructions ./proginsn per cycle;虚拟机里可能拿不到硬件计数器§6.6.13
中断占了多少cat /proc/softirqs
softirqs-bpfcc 10 1
计数在不在涨、集中在哪个核;BCC 版给出每类软中断花的时间§6.6.18
频率是不是被压着cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor书里的例子是没调过的机器停在 powersave,频率偏低;板子上默认用哪个看 BSP 的内核配置§6.9.4

mpstat、pidstat 在 sysstat 包里;perf 在 Debian 上是 linux-perf 包;带 -bpfcc 后缀的是 Debian 的 bpfcc-tools 包里的 BCC 工具,要 root。工具名的链接到本站的工具卡。常用的几个:mpstat、pidstat、vmstat、perf、runqlat、profile。

动手:在自己的机器上试

找出一个小服务把 CPU 花在哪

下面这个程序假装在处理请求:每个请求算一次校验和,再格式化一行日志。先猜一猜:两件事里谁更费 CPU?然后用 perf 量。

// hot.c:假装是一个处理请求的服务,每个请求算一次校验和、写一行日志
#include <stdio.h>
#define NOINLINE __attribute__((noinline))   // 不让编译器内联,剖析时每个函数都看得见

NOINLINE static unsigned checksum(const unsigned char *p, int n) {
    unsigned s = 0;
    for (int i = 0; i < n; i++) s = s * 31 + p[i];
    return s;
}

NOINLINE static void format_log(char *out, int id, unsigned sum) {
    snprintf(out, 256, "req=%d sum=%08x status=ok", id, sum);
}

NOINLINE static void handle(int id, unsigned char *buf, char *line) {
    for (int k = 0; k < 20; k++) buf[k] = (unsigned char)(id + k);
    format_log(line, id, checksum(buf, 4096));
}

int main(void) {
    static unsigned char buf[4096];
    char line[256];
    for (int id = 0; id < 400000; id++) handle(id, buf, line);
    puts(line);
    return 0;
}
# 保留帧指针,perf 才能顺着栈往回走(第 5 章讲为什么)
$ gcc -O2 -g -fno-omit-frame-pointer hot.c -o hot
$ perf stat -e cycles,instructions ./hot
$ perf record -F 99 -g ./hot
$ perf report --stdio --no-children

在本站的 WSL2 上,程序跑一秒多,perf 采到一百来个样本。报告里大致是这样(只留了前几行):

    93.03%  hot  hot        [.] checksum.constprop.0
     4.03%  hot  libc.so.6  [.] 0x000000000017d375
     0.95%  hot  libc.so.6  [.] snprintf
  1. 校验和占了九成以上,格式化日志的 snprintf 在 libc 里,只占几个百分点。要优化,先动 checksum。
  2. libc 那一行只有地址没有函数名:缺 libc 的调试符号。Debian 上装 libc6-dbg 后再看。缺符号是剖析里最常见的麻烦(§5.6)。
  3. 调用链里没有 handle,checksum 的上一层直接成了 main。反汇编(objdump -d hot)能看到原因:checksum 是不调用别的函数的叶子函数,编译器在 -O2 下没给它建栈帧(没有 push %rbp),perf 顺着帧指针往回走时,就从 handle 的帧直接跳到了 main。剖析里看不到某个函数,不等于它没跑;第 13 章讲帧指针回溯和 DWARF 回溯(§13.9.3)。把 #define NOINLINE 那一行后面的属性删掉(让 NOINLINE 定义为空)再编译,连 checksum 也会并进 main,报告里只剩 main。
  4. perf stat 那一行的 insn per cycle 在本机是 2.7 左右:这个循环几乎不等内存,IPC 很高。把缓冲区改成几十 MB、按跨步访问,再量一次,看 IPC 掉到多少。
WSL2
本站实测过上面四条命令(Debian 13,perf 6.12)。不用 root 就能剖析自己启动的程序;WSL2 这台机器能拿到 cycles 和 instructions 计数。全系统剖析(-a)和内核栈要 root。
服务器
对正在跑的服务用 perf record -F 99 -g -p PID -- sleep 30。服务和你是同一个用户时,paranoid 为 2 也能挂上去,只是只看用户态(本站在 WSL2 上试过);服务跑在别的用户下、或者要看内核栈,就要 root 或调低 kernel.perf_event_paranoid。云主机上硬件计数器要看实例类型。
ARM 板
perf 可以从内核源码的 tools/perf 交叉编译,或者在 Buildroot、Yocto 里选上;用帧指针回溯栈时,编译一定加上 -fno-omit-frame-pointer,不要依赖编译器的默认值。拿不到 perf 时,最粗的办法是多跑几次 cat /proc/PID/task/*/stat 看哪个线程的 utime 在涨。

让线程比核多,看饱和度长什么样

起两倍于核数的死循环,CPU 利用率会到 100%,但更值得看的是排队:每个线程有一半时间在等。

# 起 2 × 核数个忙循环,25 秒后自动退出
$ N=$(nproc); for i in $(seq $((N*2))); do timeout 25 sh -c 'while :; do :; done' & done
$ vmstat 1 5
$ pidstat -u -C sh 2 1
$ cat /proc/pressure/cpu

本站在 16 核的 WSL2 上看到的(各截了一部分):

vmstat:   r  b   ...   us sy id
         32  0   ...  100  0  0

pidstat:  %usr  %system  %wait   Command
         50.96     0.00  48.08   sh

pressure: some avg10=64.87 avg60=32.98
  1. r 是 32:16 个在跑,16 个在等。利用率早就 100% 了,它说不出「不够多少」,r 和 %wait 说得出。
  2. 每个 sh 的 %usr 和 %wait 各占一半左右:这就是调度延迟。换成真实的服务,这一半时间就加在每个请求的延迟上。
  3. PSI 的 avg10 一路往上走,avg60 跟得慢:和负载平均一样是衰减平均,看趋势要看几个窗口。
WSL2
本站实测过,不需要 root。WSL2 的 PSI 是打开的。
服务器
别在生产机上跑。要量真实服务的排队时间,用 runqlat-bpfcc 10 1(要 root)。
ARM 板
busybox 的 top 和 /proc/loadavg 一般都有;/proc/pressure/ 要内核开了 CONFIG_PSI。没有 pidstat 时,/proc/PID/schedstat 的第二个数是这个任务累计在运行队列里等待的纳秒数,隔一秒读两次相减(内核要开 CONFIG_SCHED_INFO;本站在 WSL2 上读到过非零值)。

动手:模拟器

先猜:下面这个编出来的服务,真实的 CPU 时间里 compress 占三成多。只采 10 个样本,画出来的火焰图上它会占多少?采到 300 个呢?再点「采样」验证。

模拟器

火焰图:从采样到图

每点一次「采样」,就在随机时刻打断这个服务一次,记下当时的调用栈。左边是最新的那个栈,右边是到目前为止叠出来的火焰图。点一格放大到它,搜索框里输入函数名看它出现在多少样本里。

最新一个样本(栈顶在上)

火焰图

还没有样本。点「采样 1 次」开始。
0
样本数
—
顶上最宽的函数
—
compress 占比(真实约 33%)
—
搜索命中的样本
  1. 重置后只采 10 次,记下 compress 的占比;再重置、换个时机采 10 次……其实「重置」后结果一样(随机数有固定种子),所以直接再点「采样 100 次」,看占比怎么向 33% 靠拢。样本少时,窄的函数可能根本不出现。
  2. 点 handle_req 那一格放大。它下面的 main、event_loop 变成灰色的整条:放大后只看这一棵子树,百分比是相对全部样本算的还是相对它算的?看叙述栏。
  3. 搜索 lock:lock_wait 分散在好几条路径上,单看哪一格都不宽,加起来却不小。这就是书里说的「分散但常见」的 CPU 消耗,第三条读图方法。

成书之后

默认调度器从 CFS 换成了 EEVDF

书里
讲 Linux 的分时调度时以 CFS 为准:按任务已用的 CPU 时间排在红黑树里,优先让用得少的任务上 CPU(§6.4.2)。
今天
Linux 6.6(2023 年)合入了 EEVDF(最早合格虚拟截止时间优先)调度器,取代了 CFS 的选择逻辑;6.12 完成了这次转换。读者的 WSL2 内核是 6.6,已经是 EEVDF。「运行队列」「调度延迟」这些概念和 runqlat 的量法不受影响,但书里关于 CFS 调参的细节(§6.9.3 的 sysctl)在新内核上未必还在,改之前先看自己内核的文档。
核对于 2026-10。出处:Phoronix《EEVDF Scheduler Merged For Linux 6.6, Intel Hybrid Cluster Scheduling Re-Introduced》;heise《Linux 6.12: Scheduler now expandable and EEVDF conversion complete》。

调度策略可以用 BPF 程序写了:sched_ext

书里
调度类是内核里固定的几种(书里列了 RT、O(1)、CFS、Idle、Deadline),用户只能选策略、调优先级(§6.4.2、§6.9.2)。
今天
Linux 6.12 合入了 sched_ext,一个新的调度类,允许把调度策略写成 BPF 程序加载进内核。对排查的影响:遇到调度行为不对劲,先确认机器上有没有加载自定义的调度器。
核对于 2026-10。出处:Phoronix《Sched_ext Merged For Linux 6.12 - Scheduling Policies As BPF Programs》;内核邮件列表上的 sched_ext 6.12 合并请求。

分节地图

这一章按书的固定套路走:背景(§6.1–6.3)、架构(§6.4)、方法(§6.5)、工具(§6.6)、可视化(§6.7)、实验和调优(§6.8–6.9)。上手只需要概念里讲利用率、饱和度、IPC 的几节,方法里的 USE 和剖析,工具里最常用的几个和火焰图。硬件细节和各个 BPF 工具的说明,用到时再查。

  1. CPUs drive all software and…约 300 词开篇这一章的学习目标和六个部分,扫一眼当目录用。
  2. Terminology约 200 词术语处理器、核、硬件线程、逻辑 CPU 怎么区分。
  3. Models约 100 词模型三个简单模型的引子。
  4. CPU Architecture约 100 词CPU 架构一颗四核八线程的处理器,在操作系统眼里是八个 CPU。
  5. CPU Memory Caches约 100 词CPU 缓存越靠近 CPU 越小越快。
  6. CPU Run Queues约 200 词CPU 运行队列排队的线程数是饱和度的指标;每个 CPU 一个队列的好处。
  7. Concepts约 100 词概念一组概念的引子,铺垫 IPC。
  8. Clock Rate约 200 词时钟频率为什么主频高不一定更快。
  9. Instructions约 100 词指令指令执行的五个步骤,体系结构课内容,跳过不影响后面。
  10. Instruction Pipeline约 300 词指令流水线流水线和分支预测,同上,用不到。
  11. Instruction Width约 100 词指令宽度超标量,同上。
  12. Instruction Size约 100 词指令长度x86 和 ARM 指令长度的差别,排查用不上。
  13. SMT约 200 词同时多线程超线程的两个逻辑 CPU 不等于两个核。
  14. IPC, CPI约 300 词IPC 与 CPI高低 IPC 各说明什么,该往哪个方向调。
  15. Utilization约 200 词利用率利用率怎么算,为什么 CPU 高利用率不像磁盘那样可怕。
  16. User Time/Kernel Time约 100 词用户态时间与内核态时间两者的比例透露负载类型。
  17. Saturation约 100 词饱和度满了以后线程排队;云上的限额也会造成饱和。
  18. Preemption约 100 词抢占高优先级线程插队。
  19. Priority Inversion约 200 词优先级反转低优先级线程拿着锁挡住高优先级线程;做实时的读者值得看。
  20. Multiprocess, Multithreading约 300 词多进程与多线程两种用满多核的方式的对比。
  21. Word Size约 200 词字长32 位和 64 位的差别,今天很少成为问题。
  22. Compiler Optimization约 100 词编译器优化换个新编译器有时就能变快;细节在第 5 章。
  23. Architecture约 100 词架构硬件和软件两部分的引子。
  24. Hardware约 2400 词硬件P/C 状态、各级缓存的延迟、MMU、互连、硬件计数器。要看 PMC 时回来查。
  25. Software约 1100 词软件调度器的三件事、调度类和策略、空闲线程。改调度策略前读。
  26. Methodology约 100 词方法十一种方法的总表和作者推荐的顺序。
  27. Tools Method约 200 词工具法把工具挨个跑一遍;简单,但会漏。
  28. USE Method约 300 词USE 方法对每个 CPU 查利用率、饱和度、错误;限额环境下怎么算。
  29. Workload Characterization约 400 词负载特征用几个数描述施加的负载;后面一张值得照着问的清单。
  30. Profiling约 900 词剖析定时采样和函数跟踪的区别,样本怎么处理,作者读火焰图的三步。
  31. Cycle Analysis约 300 词周期分析从 IPC 起步往下查停顿类型;要花功夫。
  32. Performance Monitoring约 100 词性能监控长期监控看哪两个指标,采样间隔为什么不能太粗。
  33. Static Performance Tuning约 200 词静态性能调优一张检查配置的问题清单:核数、频率、BIOS、微码、限额。
  34. Priority Tuning约 300 词优先级调优用 nice 给后台任务让路;实时调度类的风险。
  35. Resource Controls约 100 词资源控制固定上限和份额两种方式。
  36. CPU Binding约 100 词CPU 绑定绑核和独占 CPU 集合,换缓存热度。
  37. Micro-Benchmarking约 300 词微基准测试CPU 基准测试常常测的是编译器而不是 CPU。
  38. Observability Tools约 100 词观测工具工具总表:传统工具和基于 BPF 的工具。
  39. uptime约 700 词uptime负载平均的来历、在 Linux 上为什么含磁盘,以及 PSI。
  40. vmstat约 200 词vmstatr、us、sy、id、wa、st 各是什么。
  41. mpstat约 200 词mpstat按核看各项时间,找热核。
  42. sar约 100 词sar和 mpstat 对应的选项,能看历史。
  43. ps约 200 词psps 的 %CPU 是进程一生的平均,不是现在。
  44. top约 400 词top%CPU 不除以核数;top 自己的开销;短命进程会漏。
  45. pidstat约 100 词pidstat按进程、线程分开用户态和内核态。
  46. time, ptime约 200 词time 与 ptime从 real 减去 user 和 sys,估计等待的时间。
  47. turbostat约 100 词turbostat读处理器 MSR 的工具,只在 x86 上有;云上的虚拟机常常读不到。
  48. showboost约 100 词showboost作者为 Netflix 环境写的工具,别处未必能用。
  49. pmcarch约 200 词pmcarch同上,针对 AWS EC2 的 PMC 工具;用 perf stat 代替。
  50. tlbstat约 200 词tlbstat同上,看 TLB 的 PMC 工具。
  51. perf约 1600 词perf全系统和单进程剖析、火焰图、调度延迟、硬件事件。要剖析时回来查命令。
  52. profile约 300 词profileBCC 的剖析器,在内核里汇总,开销更低。
  53. cpudist约 100 词cpudist线程每次上 CPU 跑多久的分布。
  54. runqlat约 200 词runqlat量每次排队等了多久;一个被宿主限流的例子。
  55. runqlen约 200 词runqlen采样队列长度,比 runqlat 便宜。
  56. softirqs约 100 词softirqs每类软中断花的时间,剖析里常常看不到。
  57. hardirqs约 100 词hardirqs每个硬中断花的时间。
  58. bpftrace约 300 词bpftrace现场写单行命令问具体问题;调度器的跟踪点。
  59. Other Tools约 400 词其他工具lscpu、lstopo、cpupower、valgrind 等;GPU 工具。
  60. Visualizations约 200 词可视化为什么折线图在几千个 CPU 面前失效。
  61. Utilization Heat Map约 100 词利用率热图要成规模的机器和专门工具,单机排查用不到。
  62. Subsecond-Offset Heat Map约 200 词亚秒偏移热图同上;它的用法在 FlameScope 一节会再见到。
  63. Flame Graphs约 900 词火焰图火焰图的特征、配色、交互和读法。这一章最值得精读的一节。
  64. FlameScope约 200 词FlameScope在时间上选一小段再画火焰图,找短暂的抖动。
  65. Experimentation约 100 词实验主动测试时让 mpstat 一直开着。
  66. Ad Hoc约 100 词临时负载用一个死循环验证工具显示的对不对。
  67. SysBench约 100 词SysBench一个简单的 CPU 基准测试。
  68. Tuning约 100 词调优最大的收益来自去掉不必要的工作。
  69. Compiler Options约 100 词编译选项指向第 5 章。
  70. Scheduling Priority and Class约 100 词调度优先级和调度类nice、renice、chrt 的用法。
  71. Scheduler Options约 100 词调度器选项书里自己说很少需要调;新内核换了调度器,表里的参数未必还在。
  72. Scaling Governors约 200 词频率调节器powersave 和 performance 怎么切;作者劝:锁最高频率没有明显收益,就留在省电模式。
  73. Power States约 100 词电源状态禁用退出延迟太高的深度睡眠状态。
  74. CPU Binding约 100 词CPU 绑定taskset 的用法。
  75. Exclusive CPU Sets约 100 词独占 CPU 集合cpuset 独占和中断亲和性。
  76. Resource Controls约 100 词资源控制cgroup 的份额和带宽上限,细节在第 11 章。
  77. Security Boot Options约 100 词安全相关的启动参数关掉漏洞缓解换性能,作者自己不推荐,知道有这回事即可。
  78. Processor Options (BIOS Tuning)约 100 词处理器选项(BIOS 调优)默认值通常就是最好的;做基准测试时才关睿频。
  79. Exercises约 300 词习题习题;动手部分本页已经给了替代。

词与短语

只收先读和用到再查两档的节。斜体那一行是原句里连续的几个词,在电子书里搜它就能找到那一句。

§6.3.1 Clock Rate

a little misleading
… this can be a little misleading …
有点误导
作者往轻里说。后面紧跟着解释:主频高,等内存的周期也跟着变快,活儿并没有多干。

§6.3.8 Utilization

return of investment
… consider this a return of investment …
投资回报
常见的说法是 return on investment(ROI)。意思是:机器利用率高,钱就没白花。
degrade steeply
… performance does not degrade steeply under …
急剧变差
steeply 是「陡」:曲线陡地往下掉。CPU 到了高利用率,性能不像磁盘那样一下子掉下去。

§6.3.10 Saturation

less of a problem
… saturation is less of a problem than …
没那么严重
不是「问题更少」,是「问题的严重程度轻一些」。

§6.3.13 Multiprocess, Multithreading

outweigh
… outweighs the benefit of running across …
(代价)超过(好处)

§6.5 Methodology

a cookbook of recipes
… treat this as a cookbook of recipes …
一本菜谱
意思是:这些方法按需取用,不必全做,可以单用也可以组合。紧接着作者还是给了一个推荐顺序。

§6.5.1 Tools Method

overlook
… it can overlook issues for which …
漏掉,忽略
不是「俯瞰」。工具看不到的地方,用工具法就查不到。

§6.5.2 USE Method

a matter of
… can be a matter of checking that …
只不过是……的事
这里强调简单:查 CPU 错误,可能看看所有 CPU 是否还在线就够了。
well before
… exhaust its CPU quota well before …
远在……之前
well 在这里是程度副词,不是「好」。限额早在物理 CPU 跑满之前就用光了。

§6.5.4 Profiling

coarse
… a coarse view of CPU usage …
粗粒度的
lock-step
… to avoid lock-step sampling that may …
步调一致
原指队列踩着同一个步点行进。采样频率和某个周期性活动合拍,样本就会系统地偏向它。
prohibitive
… but the overhead can be prohibitive …
(开销)大到不能接受
plateau
Look top-down (leaf to root) for large …
平台:火焰图顶上又宽又平的一段
本义是高原。火焰图里它表示一个函数自己在很多样本里占着 CPU。
quick win
… can lead to some quick wins …
见效快的改进
win 在性能领域常指一次收益,不是「赢」。

§6.5.5 Cycle Analysis

spend some quality time
… expect to spend some quality time with …
(得)好好花一番工夫
quality time 本指陪家人的专门时间,这里是打趣:要和处理器手册长时间相处。

§6.5.6 Performance Monitoring

burst
… hide the existence of shorter bursts …
短时间的突发

§6.5.8 Priority Tuning

flak
… programs without flak from the administration.
(来自管理员的)指责
本义是高射炮火。这句是作者引的 1973 年 Unix 手册原话:用 nice 16 跑长任务,就不会挨管理员骂。

§6.5.11 Micro-Benchmarking

end up
… often end up testing differences in compiler …
结果变成了
本想测 CPU,最后测成了编译器优化的差别。

§6.6.1 uptime

toll plaza
… Imagine a car toll plaza: you …
(高速公路)收费站
headroom
… would indicate significant headroom, as it …
余量
本义是头顶上的空间。还能再加多少负载才到顶。

§6.6.2 vmstat

Perhaps one day they will be fixed
(Perhaps one day they will be fixed.)
也许哪天会修好吧
反话:第一行按说明应该是开机以来的汇总,Linux 上的 procs、memory 几列却给的是当前值,作者在挖苦这个毛病。

§6.6.3 mpstat

hot CPU
This can also identify “hot” CPUs …
热核:特别忙的那个 CPU
不是温度高,是利用率高。

§6.6.6 top

perturb
… syscalls, perturbing the system even further.
干扰(被观测的系统)
观测工具自己的开销改变了被观测的对象。

§6.6.13 perf

inundate
… rate of scheduler events may inundate …
淹没,塞满

§6.6.16 runqlat

throttled
… this instance was CPU throttled by …
被限流了
不是「被掐住喉咙」:宿主给这台虚拟机的 CPU 时间设了上限。

§6.7 Visualizations

become paint
… 10,000 lines can become paint.
糊成一片颜色
一万条折线叠在一起,什么也看不出来,只剩一层颜料。

§6.7.3 Flame Graphs

turns out to be
This turns out to be a benefit …
结果反而是
窄得写不下函数名,看起来是缺点,结果反而让人先看宽的塔。

§6.9.4 Scaling Governors

for the sake of the planet
… setting for the sake of the planet.
为了地球着想
作者劝读者:如果锁最高频率没有明显收益,就留在省电模式。

诊断题:换成你来查

场景和输出都是本站编的。先自己判断,再点选项看解释。

这组 mpstat 输出说明什么?§6.6.3

场景:一台 8 核服务器跑你的 C++ 网关,用户反馈高峰期变慢。你截了一秒的 mpstat:

CPU   %usr   %sys  %soft   %idle
all   13.1    1.2    0.3    85.4
  0    3.0    1.0    0.0    96.0
  3   98.0    2.0    0.0     0.0
  5    2.0    1.0    1.0    96.0
(其余各核和 0 号相近)

平均值把一个满载的核摊薄了。3 号核的时间几乎全在 %usr,说明是用户态代码在跑;如果是中断,会体现在 %irq 或 %soft;被偷走会体现在 %steal。下一步用 pidstat -t -p PID 1 或 top -H 找出那个线程,再对它剖析。网关里常见的是单个事件循环线程或者一把大锁让工作串行化。

负载这么高,该加 CPU 吗?§6.6.1

场景:一块 4 核 ARM 板在写日志到 eMMC,cat /proc/loadavg 显示 6.2,可 busybox 的 top 里 CPU 空闲接近 70%。ps 里有几个线程的状态是 D。

Linux 从 1993 年起把不可中断睡眠的线程算进负载平均,所以负载高不等于 CPU 不够。CPU 空闲七成、又有 D 状态的线程,指向存储:写 eMMC 慢、或者同步写太频繁。下一步看 IO 那边(第 8、9 章),内核开了 PSI 的话 /proc/pressure/io 能直接给出 IO 停顿的比例。负载平均给三个数,不只是 15 分钟的。

下面哪一种调整最有可能缓解?§6.5.2

场景:8 核虚拟机,服务的 p99 延迟在每天凌晨备份时翻倍。备份期间你看到:

vmstat:    r    us  sy  id
          18    82  17   1
pidstat:  %usr  %wait  Command
          41.0   38.5  server
          60.3   12.0  gzip
pressure: some avg10=44.10

r 是核数的两倍多,服务有近四成时间在等 CPU,PSI 也高:CPU 饱和了,抢 CPU 的是 gzip。最直接的是降低备份的优先级或限额,书里正是拿监控代理和备份当 nice 的例子(§6.5.8)。加线程只会让排队更长;绑到一个核等于把服务限制在一个核上;换更快的 CPU 也许有一点帮助,但贵,而且没有解决两件事在抢的问题。

同事这样读火焰图,对吗?§6.7.3

场景:你给服务画了一张 CPU 火焰图。左边一座窄塔顶上是 load_config,右边最宽的塔顶上是 encode_frame。同事说:「看,程序先花一点时间加载配置,然后大部分时间在编码。」

火焰图的横轴是样本的集合,按字母顺序排开,位置没有时间含义;要看时间顺序得用火焰图表(flame chart)或者 FlameScope。宽度是样本数,不是调用次数,采样根本不知道调用了几次:宽可能是因为慢,也可能是因为调用多。顶上才是采样那一刻在 CPU 上的函数,底下是祖先。

要不要换高主频的机器?§6.3.7

场景:一个查表很多的统计服务,单核 100%。你用 perf 量了它十秒:

  41,200,000,000   cycles
  11,500,000,000   instructions  #  0.28  insn per cycle

IPC 0.28 属于低的一端:每条指令平均要等三个多周期,典型原因是访存未命中。书里说这种负载换高主频的 CPU,提升达不到预期,因为等内存的时间不会跟着主频缩短(§6.3.7)。下一步用 perf stat -e cache-misses,LLC-load-misses 一类的事件确认,再看数据结构是不是太散、能不能改善局部性。「指令太多」对应的是 IPC 高的情况。

值得停下来的句子

My suggestion is to use the following, in this order: performance monitoring, the USE method, profiling, micro-benchmarking, and static performance tuning.— §6.5。本站译:我的建议是按这个顺序用:性能监控、USE 方法、剖析、微基准测试、静态性能调优。

十一种方法摆在面前,最容易的是挑自己熟的工具先跑。作者给了顺序,而且这个顺序是从便宜到贵:监控数据现成就有;USE 几分钟能扫完;剖析要采样、要符号;微基准测试要搭环境;静态调优要一项项核对配置。本站「方法」一节的七步就是照这个思路排的。

they are only a starting point, so you shouldn’t spend more than five minutes considering them before moving on to other metrics.— §6.6.1。本站译:(负载平均)只是起点,琢磨它们别超过五分钟,然后就该去看别的指标。

这句话说在负载平均那一节的开头,后面作者却花了几页讲它的来历和算法。两者不矛盾:要知道它包含什么(Linux 上包括等磁盘的线程),才知道为什么不能拿它下结论。排查时它只回答「在涨还是在落」,剩下的交给 mpstat、vmstat 和 PSI。

带走的几条

一句话:先分清忙和不够,再按核、按线程找到是谁,最后用采样把时间落到函数上。

平均值会藏住热核

8 核平均 12% 可能是一个核满载。先 mpstat -P ALL,再看总数。

§6.6.3
饱和度看 r 和 %wait

利用率到 100% 就不再增长,说不出缺多少。r 长期大于核数、%wait 不为零,才是 CPU 不够。

§6.5.2
负载平均只看趋势

Linux 上它含等磁盘的线程。三个数比一比,涨还是落,然后去看别的。

§6.6.1
%CPU 不除以核数

top(默认)、ps 里两个线程跑满就是 200%;ps 的还是进程一生的平均。

§6.6.6
剖析用 99 Hz

perf record -F 99 -g,采几十秒。缺符号就装调试符号,栈断了就保留帧指针。

§6.5.4
火焰图横轴不是时间

宽度是样本数。先看最宽的塔,再看顶上的平台,再搜分散的小格子。

§6.7.3
IPC 低,换主频没用

周期在等内存时,先查缓存和数据布局。

§6.3.7