原文:CPU Utilization is Wrong · 作者:Brendan Gregg · 发表日期:2017 年 5 月 9 日
本文为原文可见正文的完整中文译文,包含作者后续补充的“CPU 利用率真的错了吗?”一节。保留原文图示、命令与输出。文中的“现在”“较新的处理器”和云平台支持情况均指原文发表时的环境;标为“编辑注”的内容为本次整理补充。
我们都在用的 CPU 利用率指标,其实有很强的误导性,而且这种问题一年比一年严重。CPU 利用率是什么?是处理器有多忙吗?不是,它测量的并不是这个。没错,我说的就是那个人人都在用、随处可见的 %CPU 指标:每一款性能监控产品里都有,top(1) 里也有。
看到 90% 的 CPU 利用率,你可能会以为它表示:

但它实际表示的可能是:

“停顿”(stalled)是指处理器没有在指令执行上取得进展,通常发生在它等待内存 I/O 的时候。上图中忙碌与停顿的比例,是我在生产环境里经常看到的情况。你的处理器很可能大部分时间都在停顿,只是你并不知道。
这对你意味着什么?弄清 CPU 有多少时间处于停顿状态,能够帮助你决定性能优化应当着重减少代码执行,还是减少内存 I/O。任何关注 CPU 性能的人,尤其是在按 CPU 利用率自动扩缩容的云环境中工作的人,都能从了解 %CPU 中的停顿部分获益。
CPU 利用率究竟是什么?
我们称为“CPU 利用率”的指标,实际上是“非空闲时间”:也就是 CPU 没有运行空闲线程的时间。无论使用哪种操作系统,内核通常都会在上下文切换时跟踪这一状态。如果一个非空闲线程开始运行,在 100 毫秒后停止,内核就会把整个 100 毫秒都算作 CPU 被占用的时间。
这个指标的历史与分时系统一样悠久。阿波罗登月舱的制导计算机是早期分时系统的先驱之一,它把空闲线程称为“DUMMY JOB”。工程师会比较运行该任务与运行实际任务所用的周期数,把这当作计算机利用率的重要指标。我以前写过这件事。
那么,这有什么问题?
如今,CPU 的速度已经远远超过主存,而等待内存却占据了所谓“CPU 利用率”的很大一部分。当你在 top(1) 中看到很高的 %CPU 时,可能会以为瓶颈是处理器,也就是散热器和风扇下面的那个 CPU 封装。实际上,瓶颈却可能是旁边的那些 DRAM 内存条。
这一问题一直在加剧。长期以来,处理器厂商提升时钟频率的速度,超过了 DRAM 缩短访问延迟的速度,这就是“CPU 与 DRAM 之间的差距”。到了 2005 年前后、处理器时钟频率达到约 3 GHz 时,这种趋势趋于平缓。此后,处理器通过增加核心、超线程以及多插槽配置来扩展性能,这些变化都对内存子系统提出了更多需求。处理器厂商尝试用更大、更智能的 CPU 缓存,以及更快的内存总线和互连,减轻内存瓶颈。但我们通常仍在停顿等待。
怎样知道 CPU 真正在做什么?
可以使用性能监控计数器(Performance Monitoring Counters,PMCs)。它们是硬件计数器,可以通过 Linux perf 等工具读取。例如,对整个系统测量 10 秒:
# perf stat -a -- sleep 10
Performance counter stats for 'system wide':
641398.723351 task-clock (msec) # 64.116 CPUs utilized (100.00%)
379,651 context-switches # 0.592 K/sec (100.00%)
51,546 cpu-migrations # 0.080 K/sec (100.00%)
13,423,039 page-faults # 0.021 M/sec
1,433,972,173,374 cycles # 2.236 GHz (75.02%)
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
1,118,336,816,068 instructions # 0.78 insns per cycle (75.01%)
249,644,142,804 branches # 389.218 M/sec (75.01%)
7,791,449,769 branch-misses # 3.12% of all branches (75.01%)
10.003794539 seconds time elapsed
这里的关键指标是每周期指令数(instructions per cycle,输出中写作 insns per cycle,简称 IPC),它表示每个 CPU 时钟周期平均完成多少条指令。数值越高越好——这是一个简化的说法。上例中的 0.78 听起来似乎还不错:是不是意味着有 78% 的时间在忙碌?直到你意识到,这款处理器的最高 IPC 是 4.0。
这种设计也被称为 4-wide,这里指的是指令取指、译码通路的宽度。按原文的简化解释,这意味着 CPU 每个时钟周期可以退役,也就是完成四条指令。因此,在一个 4-wide 系统上,IPC 为 0.78,相当于 CPU 只达到了其最高速度的 19.5%。较新的 Intel 处理器可能会发展到 5-wide。
还有数百种 PMC 可供进一步深入分析,包括直接按不同类型测量停顿周期。
在云环境中
如果你使用的是虚拟化环境,可能无法访问 PMC,这取决于 hypervisor 是否向客户机提供支持。我最近发表的《EC2 的 PMC:测量 IPC》介绍了:在基于 Xen 的 AWS EC2 云中,专用宿主机类型现在已经可以使用 PMC。
编辑注:上一段描述的是作者在 2017 年的观察。本次没有验证当前 AWS 实例类型的支持矩阵,也没有在虚拟机上测量。应针对实际实例、虚拟化层和事件支持情况重新核验,不能把历史条件推广为今天所有云实例的能力。
如何解读,以及接下来能做什么
如果 IPC 小于 1.0,你很可能受内存停顿影响。软件优化可以从减少内存 I/O、提高 CPU 缓存利用效果以及改善内存局部性入手,尤其是在 NUMA 系统上。硬件方面,可以考虑使用 CPU 缓存更大的处理器,以及更快的内存、总线和互连。
如果 IPC 大于 1.0,你很可能受指令执行量限制。可以寻找减少代码执行的方法:消除不必要的工作、缓存操作结果等。CPU 火焰图非常适合辅助这类调查。硬件方面,可以尝试更高的时钟频率,以及更多核心或超线程。
在上述经验规则中,我用 IPC 1.0 作为分界。这个数值从哪里来?是我根据过去使用 PMC 的经验自己定的。要得到适合你自己的系统和运行时的值,可以编写两个简单的测试工作负载:一个受 CPU 限制,另一个受内存限制。测量它们的 IPC,再取二者的中间值。
编辑注:1.0 是作者明确承认的经验分界,不是跨处理器、跨运行时通用的诊断标准。应按实际 CPU 架构与工作负载建立基线,并用进一步的硬件计数器证据确认瓶颈。原文没有提供这两个测试工作负载的代码,本次也没有自行执行或编造校准结果。
性能监控产品应该告诉你什么
每一种性能工具都应该在显示 %CPU 的同时显示 IPC。或者,把 %CPU 拆分成有指令退役的周期与停顿周期,例如分别显示为 %INS 和 %STL。
至于 top(1),Linux 上还有 tiptop(1),它能够按进程显示 IPC:
tiptop - [root]
Tasks: 96 total, 3 displayed screen 0: default
PID [ %CPU] %SYS P Mcycle Minstr IPC %MISS %BMIS %BUS COMMAND
3897 35.3 28.5 4 274.06 178.23 0.65 0.06 0.00 0.0 java
1319+ 5.5 2.6 6 87.32 125.55 1.44 0.34 0.26 0.0 nm-applet
900 0.9 0.0 6 25.91 55.55 2.14 0.12 0.21 0.0 dbus-daemo
编辑注:上面是原文中的界面文本,不是本次运行截图或当前版本输出;保留了原文截断的进程名称 dbus-daemo。%INS 和 %STL 是作者提出的展示方式,不能直接假定为所有处理器都具有、且可通用相加的事件名称。
CPU 利用率具有误导性的其他原因
造成误导的并不只有内存停顿周期,还包括:
- 温度保护触发,导致处理器停顿。
- Turbo Boost 改变时钟频率。
- 内核通过 SpeedStep 改变时钟频率。
- 平均值掩盖波动:一分钟内平均利用率为 80%,可能隐藏了短时达到 100% 的峰值。
- 自旋锁:CPU 被占用,IPC 也很高,但应用在逻辑上并没有取得进展。
更新:CPU 利用率真的错了吗?
这篇文章在本站下方以及其他地方收到了数百条评论,包括 Hacker News 和 Reddit。感谢大家花时间参与,并对这个话题感兴趣。概括一下我的回复:我讨论的根本不是 iowait,那涉及磁盘 I/O;而且,只要知道自己受内存限制,就有具体可采取的优化措施,前文已经列出。
不过,CPU 利用率是真的“错了”,还是仅仅有很强的误导性?我认为,很多人会把较高的 %CPU 理解为处理单元本身就是瓶颈。正如前面所说,这种理解是错的。在那个时候,你还不知道真正的瓶颈在哪里,而它往往位于处理单元之外。
这个指标在技术上正确吗?如果 CPU 的停顿周期不能被其他工作利用,那么是否可以说它们是在“被占用着等待”——尽管这听起来有些自相矛盾?在某些情况下,的确可以说,%CPU 作为操作系统层面的指标在技术上正确,却非常容易误导人。不过,有了超线程以后,另一个线程可以利用这些停顿周期。这样一来,%CPU 就可能把实际上仍可利用的周期统计成已经占用。这就是错的。
我在本文中想重点讨论的是解读问题和可行的解决方案。但没错,这个指标在技术层面也存在问题。
你也可以说,“利用率”这个指标早就有问题了,正如 Adrian Cockcroft 此前讨论过的那样。
结语
CPU 利用率已经变成一个很容易误导人的指标:它把等待主存的周期也算了进去,而这部分周期可能在现代工作负载中占据主导。也许应该把 %CPU 改名为 %CYC,即 cycles(周期)的缩写。借助每周期指令数 IPC 等额外指标,你才能弄清 %CPU 真正意味着什么。按照本文给出的经验规则,IPC 小于 1.0 很可能意味着受内存限制,IPC 大于 1.0 则很可能意味着受指令执行量限制。
我在上一篇文章中介绍过 IPC,也介绍了测量它所需的性能监控计数器 PMC。
所有显示 %CPU 的性能监控产品——也就是所有性能监控产品——都应该同时显示 PMC 指标,解释这些利用率数值究竟代表什么,避免误导最终用户。例如,可以把 %CPU 与 IPC 放在一起显示,也可以显示有指令退役的周期与停顿周期。有了这些指标,开发者和运维人员才能更好地决定如何优化应用与系统。












暂无评论内容