http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html @木名 @雏雁

CPU Utilization is Wrong

CPU利用率我们并不陌生,但它却常常被人们误解。如果你认为它可以准确无误的刻画CPU的繁忙程度,那ä½ 就错了。那么什么是CPU利用率?请看下文分解。

可能你认为的90% CPU利用率意味着:

File:naive.jpg

而实际的可能是:

File:real.jpg

“Stalled”表示处理器流水线停顿,一般由资源竞争、数据依赖等原因造成。多数情况下表现为等待访存操作,其中又以读操作为主。在停顿周期内,不能执行指令,这意味着你的程序不往前走。值得注意的是,图中stalled状态所占的比例是作者(Brendan Gregg)依据生产环境中的典型场景计算而来,具有普遍现实意义。因此,大多时候CPU处于stalled状态,而却被你忽略了。通过进一步分析CPU Stall的原因,可以指导代码优化,提高执行效率,这是我们深入理解CPU微架构的动力之一。

What really is CPU Utilization?

我们通常所说的CPU利用率是指”non-idle time”:即CPU不执行idle thread的时间。操作系统内核会在上下文切换时记录CPU的运行时间。当一个non-idle thread开始运行,100ms后结束,内核为认为这段时间内CPU利用率100%。这种度量方式源于分时复用系统。早在阿波罗登月舱的导航计算机中,idle thread当时被叫做“DUMMY JOB”,工程师通过“运行DUMMY JOB的时间”和“运行实际任务的时间”来衡量导航系统的利用率。

So what’s wrong with this?

现在,CPU执行速度远远大于内存访问速度,等待访存的时间占了CPU时间的主要部分。当你在top中看到很高的%CPU,ä½ 可能认为处理器是瓶颈,但实际上却是内存。在很长一段时间内,CPU频率增长的速度大于DRAM访存延时降低的速度(CPU DRAM Gap)。到2005为止,处理器厂商才开始放弃“频率路线”,转向多核、超线程技术,再加上多处理器架构,这些都导致访存需求急剧上升。尽管厂商通过增大cache容量、优化cache策略、提升总线带宽来降低访存瓶颈,但我们的程序仍深受stall困扰。

what the CPUs are really doing?

在PMC(Performance Monitoring Counters)的帮助下,我们能看到更多的cpu运行状态信息。下图中,perf采集了10秒内全部cpu的运行状态。

# 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

	核心度量指标是IPC(instructions per cycle),它表示平均每个cpu cycle执行的指令数量。显然该值越大性能越好。上图中IPC为0.78,看起来还不错,是不是78% busy呢?现代处理器一般有多条流水线,运行perf的那台机器,IPC的理论值可达到4.0。如果我们从IPC的角度来看,CPU才以最高速度的19.5%(0.78 / 4.0)在运行。幸运的是,在处理器内部,有很多PMU event,可用来帮助我们分析造成stall的原因。用好PMU需要我们熟悉处理器微架构,可以参考Intel SDM。

	Best Practice

	如果 IPC < 1.0, 很可能是memory stall占主导。可从软件和硬件两个方面考虑这个问题。软件方面:减少不必要的访存操作,提升cache命中率,尽量访问本地节点内存。硬件方面:增加cache容量,加快访存速度,提升总线带宽。 如果IPC > 1.0, 很可能是计算密集型的程序。可以试图减少执行指令的数量:消除不必要的工作。火焰图CPU flame graphs,非常适用于分析这类问题。硬件方面:尝试超频、使用更多的core或hyperthread。 作者根据PMU相关的工作经验,设定了1.0这个阈值,用于区分访存密集型(memory-bound)和计算密集型(cpu-bound)程序。读者可以根据自己的实际工作平台,合理调整这个阈值。

	What performance monitoring products should tell you

	性能工具中使用%CPU时都应该附带上IPC,或者将%CPU拆分为指令执行消耗cycle(%INS)和stalled的cycle(%STL)。 对应top,在Linux系统有一个能够显示每个处理器IPC的工具tiptop:

	 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

		    Other reasons CPU utilization is misleading

		    除了访存导致的stall容易让人误解CPU利用率外,还有其他一些因素:

		    1. 温度原因导致处理器stall
		    2. Turboboost干扰了时钟速率
		    3. 内核使得时钟速率加快
		    4. 平均带来的问题:1分钟利用率平均80%,掩盖了中间100%部分
		    5. Spin Lock:CPU一直在被使用,同时IPC也很高,但是应用逻辑上并没有任何进展

		    Update: is CPU utilization actually wrong?

		    这个文章引起了大量留言(1,2,3),总结下我的回答是:这里讨论的并不是iowait(那是磁盘IO),而且如果你已经确认是访存密集型,是有些处理办法(参考上面)。

		    CPU利用率是确确实实错误的,还是只是容易误导?我认为许多人把高CPU利用率理解为处理单元是瓶颈,这是错误的。单看CPU利用率并不清楚,而且很多时候瓶颈是其他部分。这个指标技术上看是否正确?如果CPU stall的周期并不能被其他地方使用,它们是不是也就因此是“忙于等待“(听起来有点矛盾)?在有些情况,确实如此,你可以说CPU利用率作为操作系统级别的指标技术上看是对的,但是容易产生误导。然而有超线程的情况下,那些stalled的周期是可以被其他线程使用的,这时CPU%可能会将空闲的周期统计为正在使用。这种情况是错误的。这篇文章我想关注的是解释清楚这个问题,并给出解决方法建议,但确实CPU利用率存在问题。

		    你可能会说利用率作为一个指标已经不对,Andrian Cockcroft之前讨论å·²经指出过。

		    结论

		    CPU利用率已经开始成为一个容易误导的指标:它包含访存导致的等待周期,这样会影响一些新应用。也许%CPU应该重命名为%CYC(cycles的缩写)。要清楚知道%CPU的含义,需要使用其他指标进行辅助,其中就包括每周期指令数(IPC)。IPC < 1.0 多半意味着访存密集型,IPC > 1.0 多半意味着计算密集型。我在之前的文章中涵盖有IPC说明,以及用于测量IPC的Performance Monitoring Counters(PMCs)的介绍。

		    所有的性能监控产品如果展示%CPU,都应该同时展示PMC指标用于解释其真实意义,不要误导用户。比如,可以把%CPU和IPC一起放,或者说 指令执行消耗周期和stalled周期。有这些指标之后,开发者和操作者就能够知道该如何更好地对应用和系统进行调优。