可觀測性與追蹤

硬體效能計數器(在正式環境)

/ perf: PURF; PMU: P-M-U /

每顆現代 CPU 的深處,都有一小組做進矽晶裡的特殊用途計數器,像是接到特定事件上的里程表。可以叫其中一個「每完成一條指令就跳一次」,另一個「每發生一次快取未命中就跳一次」,再一個「每一次分支預測失誤就跳一次」。這個專用的硬體區塊叫效能監控單元(performance monitoring unit,PMU),維護這些計數器對 CPU 來說幾乎不花成本——它們本來就是晶片運作方式的一部分。

在 Linux 上,讀取它們的工具是 perf,它有三種日常模式。「perf stat ./prog」會跑你的程式並印出彙總的計數器總量:多少條指令、多少個週期、由此得出的每週期指令數(IPC)、快取未命中率、分支預測失誤率——這是一行就看出你的程式碼與機器契合程度的健康檢查。「perf record ./prog」用 PMU(或計時器)來取樣:每 N 個事件就抓一次當前呼叫堆疊寫進檔案,藉此學到事件「發生在哪裡」。「perf report」接著讀那個檔案,把熱門函式依序排出來。在正式環境裡,關鍵訣竅是你可以用 PID 把 perf 掛到一個「已經在跑」的行程上、甚至掛到整個系統上,取樣幾秒再卸下——觀察一個活著的服務而不必重啟,這正是可觀測性所要求的。因為這些計數器是硬體,取樣的額外負擔很低且大致固定。

它之所以重要,是因為它回答了純計時答不出的問題:不只是某函式很慢,而是它「為什麼」在硬體層面慢——是卡在等記憶體(快取未命中高)、把功夫浪費在預測失誤的分支上、還是真的是計算密集(IPC 高)?誠實的提醒:計數器及其確切意義因 CPU 型號而異,所以一個在 Intel 上代表某意義的數字,在 AMD 或 Arm 上可能代表別的;PMU 只有少數幾個實體計數器,所以一次要求很多事件會強迫多工(核心分時共用它們並外推,這會加入估計誤差);而在虛擬機或某些雲端執行個體裡,PMU 可能不可用或被限制,所以正式環境的存取並非必然。

$ perf stat ./prog 12,402,113,556 instructions # 0.61 每週期指令數(IPC) 20,331,902,114 cycles 402,118,553 cache-misses # 大量停頓 -> 記憶體受限 $ perf record -p 4821 -g -- sleep 5 # 對活著的 PID 取樣 5 秒,再卸下

perf stat 給出一行硬體健康狀況(低 IPC 加高未命中 = 記憶體受限);perf record 能對運行中的正式環境 PID 取樣後卸下。

讀懂 perf 需要對機器有個心智模型:低 IPC 加高快取未命中指向記憶體停頓,而非算術太慢——「看起來很糟」的那個計數器很少是 bug 本身,它是通往 bug 的線索。

又称
PMUperf statperf recordperformance monitoring unit效能監控單元硬體計數器