中文翻譯前言
為了理解 perf 的輸出意義[1],必須要了解 CPU Microarchitecture。如果你用 perf stat -d gzip file1 這個方式來輸出,你會發現到輸出中有加粗這個部份:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
➜ /tmp perf stat –d gzip files Performance counter stats for ‘gzip files’: 6990.942695 task–clock:u (msec) # 0.996 CPUs utilized 0 context–switches:u # 0.000 K/sec 0 cpu–migrations:u # 0.000 K/sec 112 page–faults:u # 0.016 K/sec 22,653,310,816 cycles:u # 3.240 GHz (33.32%) 9,561,514,420 stalled–cycles–frontend:u # 42.21% frontend cycles idle (44.42%) 4,117,085,985 stalled–cycles–backend:u # 18.17% backend cycles idle (44.40%) 46,685,464,045 instructions:u # 2.06 insn per cycle # 0.20 stalled cycles per insn (55.50%) 5,335,420,173 branches:u # 763.190 M/sec (55.53%) 4,931,709 branch–misses:u # 0.09% of all branches (55.57%) 8,330,651,925 L1–dcache–loads:u # 1191.635 M/sec (55.51%) 157,090,515 L1–dcache–load–misses:u # 1.89% of all L1-dcache hits (22.32%) 3,468,380 LLC–loads:u # 0.496 M/sec (22.26%) <not supported> LLC–load–misses:u 7.021395514 seconds time elapsed |
其中有 stalled-cycles-frontend:u 以及 stalled-cycles-backend:u 兩個 events 可做參考。如果要更為理解 CPU frontend 以及 backend,可以參考 Shannon Cepeda’s 的 frontend and backend. 因此有了這兩篇文章的翻譯。
正文
我相信你一定知道,現代電腦處理器有使用一種技術叫作 pipelining (管線化、流水化) 來提升指令吞吐量。在一個管線中,處理器裏各種不同的硬體專用部件 [various dedicated pieces] 分別負責執行每一個指令的特定功能,或是在同一時間處理不同的指令。 舉例而言,當管線的一個部份在執行 [executing] 指令 A,另一個部份則去取得 (fetching) 指令 B,再另一個部份可能正在提交 [committing[2]] (把結果寫入記憶體)指令 C。這樣的設計,允許處理器在同一個時間內處理多個指令,同時讓等待資料與其他長指令的處理更為平順。
管線並不是一個存在於晶片上的實體架構 ─ 而是指實際上處理器處理指令的流程。特規化的硬體與隊列 [queues] 設計讓管線能夠持續的把指令送到正確的方向。管線的階層 [stages] 以及實作細節在不同 CPU 型號上可能有所不同。舉例而言,Intel Nehalem 架構的管線設計就跟 Intel Sandy Bridge 有所差異。 (事實上,管線的佈線 [Layout]、階層的數量、每一個階層的佈局方式…etc,都是每個新命名的架構的差異重點。
儘管這看起來很抽象與變化多端,我們還是發展出幾個管線技術的通用名詞。熟悉管線化以及她的相關術語對於效能最佳化是很有幫助的。大多數的管線 ─ 包括 Sandy Bridge 的管線 ─ 都具有兩個重要的部份 ─ 前端以及後端 [front-end and the back-end]。
管線的前端
The Front-End of the Pipeline
管線的第一個部份通常負責提供一個工作流 [stream of work] 給後端來執行。在 Intel x86-based 的處理器 (包括基於 Sandy Bridge 的處理器),前端是有序 (in-order) 而大多數後端不是。這意味著,在管線開始的時候,指令的處理順序跟他們在程式被發現的順序是一樣的。同時在 Intel x86 處理器中,前端是工作在指令之上 (以組合語言的形式),而後端是工作在微指令[3] [micro-operations / μops]上。Intel x86 處理器使用的是 CISC 架構,這表示在管線中,組語指令將被拆分成更小的片斷,也就是微指令。前端的工作就是把指令拆解成微指令 ─ 稱做對指令 “解碼” [called “decoding” the instructions.]。
所以對於 x86-based 的處理器來說,前端最主要的工作就是這兩件事情 ─ 取得指令 (fetching instructions) (從存放在記憶體或是快取中的二進位程式碼),以及把指令解碼成微指令。作為 fetching process 的一部份,前端也必須要在遇到分支指令 (if-type statements) 的時候去預測分支的目標,好讓它知道該去哪個地方擷取下一個指令。五花八門的特規邏輯以及硬體合力完成這些功能 ─ 一個分支預測器、一個特規微指令快取、特別是對簡單與複雜指令的解碼器等等。這些硬體結合起來只為了達到前端的主要目標 ─ 提供後端微指令。Sandy Bridge 的前端可以在每個週期 (或是處理器 clock-tick) 提供 4 個微指令給後端。
Reference
- Perf – CPU Microarchitechture: http://www.brendangregg.com/perf.html#CPUstatistics
- CSE 502:Computer Architecture – Instruction Commit: https://compas.cs.stonybrook.edu/course/cse502-s13/lectures/cse502-L11-commit.pdf
- Micro-operations: https://en.wikipedia.org/wiki/Micro-operation
- Pipeline Speak: Learning More About Intel Microarchitecture Codename Sandy Bridge: https://software.intel.com/en-us/blogs/2011/11/22/pipeline-speak-learning-more-about-intel-microarchitecture-codename-sandy-bridge
Leave a Reply