原文是性能优化大神Brendan Gregg的一篇17年的博客”CPU Utilization is Wrong”[1],写的很有参考价值,我翻译一下。Youtube上也有与之对应的一个presentation:
我们大家用的“CPU占用率”这个指标存在很大的误导性,而且这种误导近年来越来越严重。什么是CPU占用率,它是指你的处理器到底多忙吗?不,那并不是它所能反映出的。是的,我正要讨论%CPU
这个大家都处都在用的性能指标(包括每一种的性能检测软件,包括top工具)。
你以为90%的CPU占用率是这样时,
其实它有可能是这样的:
Stalled指的是处理器不能继续执行指令,这基本是由于CPU在等待内存IO。上边我所画的busy和stalled的比例正是我在真实的生产环境所见的。所以,很有可能你在内存stall时,并没有察觉到。
对我们来说这意味这什么?明白了我们的CPU有多少被stall可以让我们直接将性能优化的努力放到减少不必要的代码和内存IO。能搞清楚CPU占用率中有多少比例的stall成分,任何关注CPU性能的人(尤其当程序运行在是根据CPU来自动扩展的云环境中时)都能获得好处。
到底什么是CPU占用率
CPU占用率这个指标其实就是“非空闲时间”,即CPU没有在跑idle线程。OS内核经常会在上下文切换时进行追踪操作,如果非空闲线程开始跑并在100毫秒后停下来,那么内核认为CPU被全部占用。
这个指标和分时系统一样古老。阿波罗登月舱的指挥计算机(分时系统的先驱)把它的idle线程称为”哑任务(dummy job)”,工程师们通过追踪运行哑任务和其他运行真实任务的cycles来得到这一重要的计算机占用率指标。(就像我以前所写一样。)
那么问题出在哪呢?
当今,CPU比主存更快,因此“等待内存”成为了“CPU占用率”的主要部分。当你在top工具中看到高的%CPU
时,你可能会认为处理器(在散热器和风扇下的CPU封装)是瓶颈,不过其实都是那些DRAM内存的问题。
这种情况越来越糟,一直以来,处理器时钟的增速都要大于DRAM访问延迟的增速(“CPU-DRAM鸿沟”,译注:也叫“内存墙”)。这种局面持续到2005年的3 GHz处理器,从那以后,处理器往多核、超线程、多socket发展,这些都提升了对内存子系统的需求。处理器厂商尝试用更智能的CPU cache、更快的内存总线和互联网络来解决这些问题。但是我们仍然经常面临stall问题。
怎么知道CPU正在干什么
通过使用性能监测计数器(Performance Monitoring Counter, PMC) :一种可以用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时钟周期我们平均完成的指令数。IPC这个值越高越好,例子中值为0.78看起来不错,这是表示78%的占用吗?不是的,这个处理器最高可以达到4.0的IPC(这也被称为4-wide处理器,用来说明指令fetch/decode的路径)。这说明,CPU每个时钟周期可以retire(完成)4个指令。所以在一个4-wide处理器上,0.78的IPC说明当前CPU只是它最高运行速度的19.5%。最新的Intel处理器可以达到5-wide。
PMC有上百个,你可以利用它们来深挖性能瓶颈,比如直接测量不同类型的stall周期。
云环境中
如果你在虚拟环境中,你可能访问不到PMC,这取决于你的hypervisor支持不支持。我最近写过The PMCs of EC2: Measuring IPC这篇文章,说明了怎么在AWS EC2 Xen-based的云主机上使用特定PMC的。
一些解释和经验
如果IPC < 1.0,那么很可能是memory stalled,软件调优策略可以考虑减少内存IO,提升CPU cache或内存的局部性,特别是在NUMA系统中。硬件调优可以考虑有大CPU cache、更快内存、总线和互联网络的处理器。
如果IPC > 1.0,那么很可能是“instruction bound”,可以考虑减少代码的执行,即减少不必要的任务和cache操作等。CPU火焰图(CPU flame graphs)是一个很好的分析工具。对于硬件调优,可以尝试更快的时钟频率、更多的核或超线程。
对于我们以上的规则,我用1.0作为IPC分界,为啥这样做?这是基于我以前有关PMC的工作。你也可以找到一个适合你的系统和环境的值:只需要写两个样例负载程序,一个是CPU bound,另一个是memory bound,然后测下它们各自的IPC,取一个中间点。
性能检测工具应该告诉你什么?
每种性能检测工具除了%CPU
外,都还应该展示出IPC。或者将%CPU
分解为”instruction-retired cycles”和“stalled cycles”,比如可以称为%INS
和%STL
。
对应于top工具,Linux有tiptop工具,展示了进程的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
证明CPU占用率具有误导性的其他原因
并非只有memory stall导致了CPU占用率的误导性,还有其他原因,包括:
- 高温跳闸导致的处理器stall;
- Turboboost(睿频)导致的时钟频率不稳;
- 内核根据动态调整导致的时钟频率变化;
- 求平均值导致的问题:超过一分钟的80%占用,掩盖了100%的爆发占用;
- Spin locks:CPU是被占用,也有很高的IPC,但程序并无逻辑上的进展。
更新:CPU占用率到底错了没有
现在已经有上百条评论,感谢每个花时间看和感兴趣这个话题的人。总结下我的回复:我一直都没有说iowait(那是磁盘IO),而且如果你知道负载是memory bound,上边也给出了优化建议。
但是CPU占用的确是错的,或者说存在严重的误导性?我认为很多人认为高%CPU意味着处理器单元是瓶颈,正如我之前所说,这是不对的。因为你不能只因CPU占用而做出这个断定,很可能是其他外部原因导致的。这个指标技术上正确吗?如果CPU stall的那些cycles不能挪为它用,他们不就是“被占用的等待”吗(听起来像是个矛盾)?某种意义上来说是这样的,你可以说%CPU
是在OS层次上是一个正确的指标,但是它任然有很强的误导性。如果有hyperthread,这些停滞的cycles是可以被用于其他线程的,所以%CPU
可能会统计到其实是还可用stalled cycles。所以这个指标就是不对的,这篇文章之我只想解释为题和给出建议,但这个指标技术上确实是有问题的!
你可以说占用率已经不该作为一个指标被使用了,就像Adrian Cockcroft之前所说那样。
结论
CPU占用率已成为一个具有严重误导性的性能指标:它包括了等待内存的cycles,这在当今的负载中很常见。也许%CPU
应该重命名为%CYC
,即cycles的简写。通过其他一些检测指标(包括IPC),你可以搞明白%CPU
真正代表了什么,如果IPC小于1表示内存bound,如果大于1表示instruction bound。我在之前的文章也介绍过IPC和测量他相关的PMC。
性能监测工具除了展示%CPU
还应当展示一些PMC指标来进一步说明CPU占用率代表这什么,这就会导致过多的误解。比如可以同时展示%CPU
和IPC
,也可以把%CPU
分解成instruction-retired cycles和stalled cycles。有了额外这些监测指标,开发和运维人员都可以更好地优化他们的应用和系统。
[1] CPU Utilization is Wrong, http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html
[2] What does an idle CPU do? https://manybutfinite.com/post/what-does-an-idle-cpu-do/