鄭建勳:Go程式效能分層優化 | CPU篇
責編 | 韓楠
約 5, 500 字 | 11 分鐘閱讀
效能問題,是所有程式和系統隨著時間都可能面臨的問題,雖然本文中以Go程式作為切入點,但其思考方式,我認為仍然適用於其他語言編寫的系統,並有啟發意義。
不過早優化 ≠ 不優化
圖1 拉布雷亞瀝青坑 (圖源:維基百科)
效能優化是成本與收益的權衡
一、效能優化分層抽象
圖2 效能優化分層抽象
(一)系統級別
圖3 系統級別優化考慮因素
圖4 單體服務(左) VS 微服務(右)
(圖源:《Kubernetes in Action》[5] )
(二)程式設計和組織
圖5 程式設計和組織考慮因素
(三)程式碼實施
圖6 程式碼實施階段
這一層要考慮的效能內容,包括了遵守常見的程式碼規範,為程式某一模組選擇正確合適的演算法,降低複雜度解決我們面臨的問題,例如二分搜尋、快速排序、合併排序、map-reduce等等。
也包含了選擇何種資料結構,例如陣列、雜湊對映、連結串列、堆疊、環形佇列等。還包括了增加快取、提高快取命中率、優化程式結構、減少鎖爭用甚至無鎖。甚至包括了標準庫、執行時與編譯時的調優。
結合下面這圖來看下,在Go1.14中,對執行時對於記憶體管理進行了優化,將原先基於Treap平衡樹的記憶體分配演算法,替換為了基於點陣圖的基數樹分配方法[6],解決鎖爭用問題,加速了併發的記憶體分配。
圖7 Go1.14記憶體管理基數樹結構
(四)作業系統級別
當今的軟體,通常不會直接在機器硬體上直接執行,相反,我們通過作業系統將程式分為了多個執行緒,並將執行緒排程到 CPU 上執行。
作業系統排程、硬/軟中斷、執行緒上下文切換等因素,都與程式效能息息相關。同時作業系統提供了其他服務,如記憶體和 IO 管理、裝置訪問等。
(五)硬體級別
圖9 具有多核CPU和統一記憶體訪問 (UMA) 的高階計算機體系結構 (來自《Efficient Go》)
二、Go語言協程執行模型
GMP模型瞭解Go排程模型
圖10 GMP模型含義
Go語言為了方便協程排程與快取考慮,抽象出了邏輯處理器P。G、M、P之間的對應關係可對照下圖來看。在任一時刻,一個P可能在其本地包含多個G,同時,一個P在任一時刻只能繫結一個M。
協程上下文切換速度明顯快於執行緒
協程的速度要快於執行緒,其原因在於:
上下文切換的速度受到諸多因素的影響,這裡列出一些值得參考的量化指標: 執行緒切換的速度大約為1~2 微秒,Go 語言中協程切換的速度比它快數倍,為0.2 微秒左右。
圖12 執行緒切換 VS 協程切換
(圖源:《Go底層原理剖析》[8] )
三、系統級別優化
分散式系統設計是另一門艱深的學問
系統效能監控必不可少
四、程式內部設計和組織優化
非同步化求快速返回
並行化縮短關鍵路徑
併發模型發揮執行時的最大威力
網路I/O底層多路複用
在實際中,很多專案是基於tcp、http這樣的網路伺服器,這時候需要掌握Go語言的網路模型。Go原生的網路庫每一個請求,都會新建立一個協程。
磁碟I/O同步阻塞
多種手段應對磁碟I/O堵塞
無鎖化減少並行的溝通成本
由於鎖和通道導致請求耗時增加的情況,可以通過pprof觀察到。
go tool pprof
go tool pprof
注意:我們一般不會考慮標準庫和執行時中鎖帶來的瓶頸問題,只有在非常大量的併發訪問下,例如上萬次qps,才會考慮標準庫可能面臨的鎖競爭問題。
驗證並行效率,做到心中有數
圖17 獲取協程數量的幾種方式
圖18 排程器分析
GODEBUG=schedtrace=1000,scheddetail=1 ./main
curl -o trace.out
圖20 trace檢視排程細節
結語
▼
在本文中,程式面臨的任何效能優化問題,都可對應到系統設計到硬體層面的5層抽象模型中。
自上而下逐個擊破,找到對應的設計思路,瓶頸問題,觀察指標、排查手段和解決方法。 這將幫助你更早地規避效能問題、更快地定位效能問題、更有效地解決效能問題。
對於本文的知識,總地來說,效能分析與優化在思考上要抽象分層逐個擊破、在系統設計上要合理拆分與組合、在程式設計上要非同步化、求並行、無鎖化、設計和系統特性匹配的併發模型。
圖21 全文思維導圖
(可幫助你回顧內容要點)
參考資料:
[1] 過早的效能優化是萬惡之源,檢視這句話的上下文:《Structured Programming With Go To Statements》by Donald Knuth
[2] 人月神話是專案管理的經典書籍,論述焦油坑《Mythical Man-Month》
[3] 效能的定義:
[4] 效率優化的級別:《Efficient Go》
[5] 單體服務 VS 微服務:《Kubernetes in Action》
[6] go1.14記憶體分配基數樹:
[7] linux排程器原理:https://developer.ibm.com/tutorials/l-completely-fair-scheduler/
[8] Go排程器原理,可以參考筆者的著作:《Go語言底層原理剖析》
[9] 分散式系統設計原理:《Designing Data-Intensive Applications》
[10] Go語言網路模型:
[11] 檔案buffer與直接寫入效能對比:https://www.instana.com/blog/practical-golang-benchmarks/#file-i-o
[12] io堵塞導致建立上萬個執行緒,panic的案例與原理: https://mp.weixin.qq.com/s/0nwe-YrMGrl2futS5wkT6A
[13] Go執行時metric指標詳解: https://mp.weixin.qq.com/s/4mFFbzrviLWViws8NbRzbA
[14] 排程器列印指標解釋:https://www.ardanlabs.com/blog/2015/02/scheduler-tracing-in-go.html
[15] 系統效能的思考方式、指標與工具《Systems Performance, 2nd Edition》
THE END
轉載請聯絡ITPUB官方公眾號獲得授權
—————————————————————————————————
歡迎各領域技術人員投稿
投稿郵箱 | hannan@it168.com
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70016482/viewspace-2902942/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 鄭建勳:程式碼優化的三重境界優化
- Linux 效能優化之 CPU 篇 ----- 殭屍程式Linux優化
- Linux 效能優化之 CPU 篇 ----- 套路篇Linux優化
- 效能優化-合成層優化
- 效能優化篇優化
- Linux 效能優化之 CPU 篇 ----- 上下文切換Linux優化
- Linux 效能優化之 CPU 篇 ----- Linux 軟中斷Linux優化
- Linux效能優化實戰CPU篇之總結(四)Linux優化
- 效能除錯:分析並優化 Go 程式除錯優化Go
- Linux CPU 效能優化指南Linux優化
- iOS圖層效能優化iOS優化
- Linux效能優化實戰CPU篇之軟中斷(三)Linux優化
- IOS效能優化篇iOS優化
- Android效能優化篇:從程式碼角度進行優化Android優化
- Android效能優化篇之計算效能優化Android優化
- Go語言效能優化- For Range 效能研究Go優化
- iOS 效能篇一一UITableView效能優化iOSUIView優化
- Go工程管理 19 | 效能優化:Go 語言如何進行程式碼檢查和優化?Go優化行程
- 前端效能優化JavaScript篇前端優化JavaScript
- Android效能優化(1)—webview優化篇Android優化WebView
- 效能優化小冊 - 分類構建:利用好 webpack hash優化Web
- .NET程式的效能要領和優化建議優化
- Gse v0.20.0 釋出了, Go 高效能分詞, 優化效能和程式碼, 更多測試GseGo分詞優化
- Android效能優化篇之服務優化Android優化
- 效能優化開篇綜述優化
- 效能優化篇 - Performance(工具 & api)優化ORMAPI
- PHP效能優化 -理論篇PHP優化
- UITableView效能優化-中級篇UIView優化
- webpack--效能優化之打包構建速度和程式碼除錯優化Web優化除錯
- MySQL分頁效能優化指南MySql優化
- 如何優化程式效能優化
- oracle 效能優化建議小結Oracle優化
- Linux 效能優化之 IO 篇Linux優化
- Linux 效能優化之 cup 篇Linux優化
- 前端效能優化之路——圖片篇。前端優化
- Spark效能優化指南:高階篇Spark優化
- Spark效能優化指南:基礎篇Spark優化
- jQuery高階技巧——效能優化篇jQuery優化