# 引言
可能你認為的 90% CPU 利用率意味著這樣的情形:

而實際卻可能是這樣的

CPU 并非 90% 的時間都在忙著,很大一部分時間在等待,或者說“**停頓(Stalled)**”了。這種情況表示處理器流水線停頓,一般由**資源競爭、數據依賴**等原因造成。多數情況下表現為等待訪存操作,其中又以**讀操作為主**。在停頓周期內,**不能執行指令**,這意味著你的程序不往前走。值得注意的是,圖中 “Stalled” 狀態所占的比例是作者依據生產環境中的典型場景計算而來,具有普遍現實意義。因此,大多時候 CPU 處于停頓狀態,而你卻不知道,因為 CPU 利用率這個指標沒有告訴你真相。通過進一步分析 CPU 停頓的原因,可以指導代碼優化,提高執行效率,這是我們深入理解CPU微架構的動力之一。
# CPU 利用率的真實含義是什么?
我們通常所說的CPU利用率是指 “**non-idle time**”:即CPU不執行 idle thread 的時間。操作系統內核會在上下文切換時記錄CPU的運行時間。假設一個 non-idle thread 開始運行,100ms 后結束,內核會認為這段時間內 CPU 利用率為 100%。這種度量方式源于分時復用系統。早在阿波羅登月艙的導航計算機中,idle thread 當時被叫做 “DUMMY JOB”,工程師通過比對運行 “DUMMY JOB” 和 “實際任務” 的時間來衡量導航系統的利用率。
那么這個所謂“利用率”的問題在哪兒呢?
當今時代,CPU 執行速度遠遠大于內存訪問速度,等待訪存的時間成為占用 CPU 時間的主要部分。當你在 top 中看到很高的 “%CPU”,你可能認為處理器是瓶頸,但實際上卻是內存。在過去很長一段時間內,CPU 頻率增長的速度大于 DRAM 訪存延時降低的速度(CPU DRAM gap),直到2005年前后,處理器廠商們才開始放棄“頻率路線”,轉向多核、超線程技術,再加上多處理器架構,這些都導致訪存需求急劇上升。盡管廠商通過增大 cache 容量、優化 cache 策略、提升總線帶寬來試圖緩解訪存瓶頸,但我們的程序仍深受 CPU stall 困擾。
# 如何真正辨別 CPU 在做些什么?
在 PMC(Performance Monitoring Counters) 的幫助下,我們能看到更多的 CPU 運行狀態信息。下圖中,`perf`?采集了10秒內全部 CPU 的運行狀態。
```bash
# perf stat -a -- sleep 10
Performance counter stats for 'system wide':
320075.987964 cpu-clock (msec) # 31.997 CPUs utilized
75,544 context-switches # 0.236 K/sec
4,044 cpu-migrations # 0.013 K/sec
668 page-faults # 0.002 K/sec
6,330,615,683 cycles # 0.020 GHz
2,201,747,556 instructions # 0.35 insn per cycle
447,782,180 branches # 1.399 M/sec
26,550,335 branch-misses # 5.93% of all branches
10.003433562 seconds time elapsed
```
這里我們重點關注的核心度量指標是 **IPC(instructions per cycle)**,它表示`平均每個 CPU cycle 執行的指令數量`,很顯然該數值越大性能越好。上圖中 IPC 為 0.78,看起來還不錯,是不是 78% busy 呢?現代處理器一般有多條流水線,運行?`perf`?的那臺機器,IPC 的理論值可達到 4.0。如果我們從 IPC? 的角度來看,這臺機器只運行到其處理器最高速度的 19.5%(0.78 / 4.0)。幸運的是,在處理器內部,有很多 PMU event,可用來幫助我們分析造成 CPU stall 的原因。用好 PMU 需要我們熟悉處理器微架構,可以參考 Intel SDM。
# 最佳實踐是什么?
如果 IPC < 1.0, 很可能是 Memory stall 占主導,可從軟件和硬件兩個方面考慮這個問題。軟件方面:減少不必要的訪存操作,提升 cache 命中率,盡量訪問本地節點內存;硬件方面:增加 cache 容量,加快訪存速度,提升總線帶寬。
如果IPC > 1.0, 很可能是計算密集型的程序。可以試圖減少執行指令的數量:消除不必要的工作。火焰圖CPU flame graphs,非常適用于分析這類問題。硬件方面:嘗試超頻、使用更多的 core 或 hyperthread。作者根據PMU相關的工作經驗,設定了1.0這個閾值,用于區分訪存密集型(memory-bound)和計算密集型(cpu-bound)程序。讀者可以根據自己的實際工作平臺,合理調整這個閾值。
# 性能工具應該告訴我們什么?
作者認為,性能工具中使用 %CPU 時都應該附帶上 IPC,或者將 %CPU 拆分為指令執行消耗 cycle(%INS) 和 stalled 的 cycle(%STL)。對應到?`top`,在 Linux 系統有一個能夠顯示每個處理器 IPC 的工具?`tiptop`:
- 目錄
- 離散的內容
- IO模型
- 網卡綁定
- ssh
- 硬件測試
- 硬件
- limits
- 網絡流量
- 硬盤IO
- 硬盤
- tmux
- 主機名和域名
- http_proxy
- iptables
- 內核參數
- 塊設備和字符設備
- 內存
- 虛擬內存并非交換分區
- 網絡延時
- 概念
- 多核壓縮
- linux基礎
- SSH協議
- 軟件管理
- yum
- 制作本地源 yum系列
- 制作本地源 apt系列
- apt
- 在 Linux 中移除從源代碼安裝的程序的一種簡單的方法
- 其他
- 源碼編譯和二進制安裝后更改配置
- DNS
- bind
- 守護進程
- 特殊權限
- limit.conf配置
- 網絡
- shell-ok
- 變量ok
- 數組ok
- 系統變量和環境變量
- 運算符和計算-ok
- 條件測試-ok
- 選擇-ok
- shell循環-ok
- 輸出echo和printf-ok
- 技巧-ok
- pre-web
- http協議
- web服務器
- Apache
- apache安裝
- yum安裝
- 二進制安裝
- 編譯安裝
- httpd命令
- 運行 監控apache
- apache配置文件
- 常用配置
- MPM多處理模塊
- 編譯模塊
- apache模塊
- apache核心模塊
- apache標準模塊
- apache第三方模塊
- 虛擬主機
- 1
- CGI-FastCGI-SSI
- 別名和重定向
- apache應用
- 301重定向
- apache防盜鏈
- http轉化為https
- 訪問時間段控制
- 控制訪問目錄
- 限制指定USER_AGENT
- 不同客戶端訪問不同網頁
- apache黑名單
- httpd之禁止解析php
- 不記錄css/js/img的訪問日志
- 瀏覽器端靜態緩存
- apache訪問日志自動切割
- order-require
- 壓縮傳輸
- httpd-ssl
- apache代理
- 正向代理
- 反向代理
- apache調優
- httpd壓力測試工具ab
- CGI測試
- php
- php原理
- httpd和php的結合方式
- php yum安裝之DSO模式
- php 編譯安裝之DSO模式
- php-fpm詳解
- php yum安裝之php-fpm模式
- php 編譯安裝之FastCGI模式
- php擴展之mysql
- php擴展之gd
- php擴展之pcntl
- php擴展之xcache
- php擴展之ZendGuardLoader
- phpMyAdmin
- wordpress
- 數據庫-mysql
- 數據庫原理
- mysql數據庫原理
- mysql源碼編譯安裝
- mysql二進制包安裝
- mysql命令行工具
- 更改密碼
- 數據庫授權grant
- mysql日志
- 命令
- 常用
- 小命令大作為
- awk 報告生成器
- 網絡命令
- 命令查找
- 壓縮歸檔命令
- 文件管理
- 文件管理命令
- 文件查看命令
- 目錄管理命令
- 用戶管理命令
- 用戶權限管理
- curl
- cheat
- chrony
- command
- crontab任務計劃
- cut
- date
- dd
- df
- echo
- find
- grep
- hash
- iftop
- kill pkill killall
- ls
- lsmod和modprobe
- lsof
- man
- mkpasswd
- mount
- mtr
- netstat
- nmap
- nc
- NTP
- passwd
- rm
- rdate
- pv
- sar系統活動情況報告
- sed文本處理命令
- setup
- screen
- shutdown
- sort 命令
- sudo
- tcpdump
- top
- uniq
- wget
- who
- xargs
- 監控
- zabbix郵件報警
- Redis
- redis安裝
- redis數據類型和操作方法
- redis持久化和配置
- redis主從配置
- php連接redis
- redis實現session共享
- 安裝測試
- redis設置密碼
- ELK日志分析系統
- elasticsearch
- logstash
- logstash插件
- filebeat日志收集
- kibana
- jenkins
- jenkins安裝與配置
- 案例1
- 案例2
- 案例3
- 代碼倉庫之svn
- svn服務端配置
- 常用操作
- svn備份
- LB集群
- LVS負載均衡集群
- ipvsadm使用方法
- LVS調度方法
- NAT原理
- NAT實踐
- DR原理
- DR實踐
- TUN原理
- LVS持久連接
- HA集群
- HPC集群
- 共享存儲
- ftp協議
- vsftpd
- NFS
- 網站架構發展
- 文件同步
- rsync基本用法
- rsync安裝和使用_拉取模式
- lsyncd安裝和用法
- zabbix
- zabbix服務端安裝
- zabbix客戶端安裝
- zabbix編譯安裝
- zabbix監控tomcat
- zabbix監控mysql
- gitlab
- supervisor
- nsq
- ruby
- nodejs
- consul
- mesos
- zookeeper
- rwho
- 對象存儲
- 工具
- rclone
- minio
- linux 性能調優
- CPU
- 第一部分 CPU
- 安全