Go 高效能系列教程之六:一些建議和實踐
原文連結 https://dave.cheney.net/high-performance-go-workshop/gophercon-2019.html#tips-and-tricks)
這是一些隨機的建議。也是該系列的最後一部分,包含一些微小的優化程式碼的技巧。
6.1 協程
Go 語言中最關鍵的特性就是協程
協程非常易用、建立成本低,你幾乎可以認為是沒有代價的。
Go 的執行時是為擁有成千上萬個協程的程式而編寫的,一個程式包含成千上萬的協程並不奇怪。
但是,每個協程都需要一個最小的協程棧的記憶體空間,目前最小記憶體空間是 2kb。
2048 * 1,000,000 goroutines = 2GB 記憶體,而且他們還沒有做任何事情。
6.1 要知道何時停止 goroutine
協程的的建立和執行的成本非常低。但就記憶體佔用而言,他們的成本確實有限,但也不能建立無限個協程。
在你的程式中,每次你使用go關鍵詞就能啟動一個協程,但你必須要知道如何,以及何時該協程能結束。
在你的程式設計中,某些協程可能會執行直到程式結束才會退出。這些協程非常罕見,但也要遵循該規則)。
如果你不知道何時結束協程,那將會有潛在的記憶體洩漏的隱患。因為協程會將其記憶體以及從棧上可以訪問的任何堆上分配的變數固定在堆記憶體上。
注意:永遠不要開啟一個不知道如何停止的協程
6.1.2 深入閱讀
- Concurrency Made Easy video
- Concurrency Made Easy slides
- Never start a goroutine without knowning when it will stop(Practical Go, QCon Shanghai 2018)
6.2 對於某些請求,Go 使用高效的網路輪詢演算法
Go 執行時使用有效的作業系統輪詢機制(kqueue,epoll,windows IOCP 等)來處理網路 IO。 一個單一的作業系統執行緒將為許多等待的 goroutine 提供服務。
但是,對於本地檔案 IO,Go 不會實現任何 IO 輪詢。 * os.File 上的每個操作在進行中都會消耗一個作業系統執行緒。
大量使用本地檔案 IO 可能導致您的程式產生數百或數千個執行緒。 可能超出您的作業系統所允許的範圍。
您的磁碟子系統不希望能夠處理成百上千的併發 IO 請求。
要限制併發阻塞 IO 的數量,請使用工作程式 goroutine 池或緩衝的通道作為訊號燈。
var semaphore = make(chan struct{}, 10) func processRequest(work *Work) { semaphore <- struct{}{} // acquire semaphore // process request <-semaphore // release semaphore }
6.3 當心應用程式中的 IO 因子
如果您正在編寫伺服器程式,則它的主要工作是多路複用通過網路連線的客戶端和儲存在應用程式中的資料。
大多數伺服器程式都會接受請求,進行一些處理,然後返回結果。 這聽起來很簡單,但是根據結果,它可能會讓客戶端消耗伺服器上大量(可能是無限制的)資源。 這裡有一些注意事項:
- 每個傳入請求的 IO 請求數量; 單個客戶端請求生成多少個 IO 事件? 它可能平均為 1,或者如果從快取中提供了許多請求,則可能小於一個。
- 服務查詢所需的讀取量; 它是固定的,N + 1 還是線性的(讀取整個表以生成結果的最後一頁)。
相對而言,如果記憶體很慢,那麼 IO 太慢了,您應該不惜一切代價避免這樣做。 最重要的是,避免在請求的上下文中進行 IO-不要讓使用者等待磁碟子系統寫入磁碟甚至讀取磁碟。
6.4 使用流式 IO 介面
儘可能避免將資料讀入 [] byte 並將其傳遞。
根據請求,您可能最終將兆位元組(或更多!)的資料讀取到記憶體中。 這給 GC 帶來了巨大壓力,這將增加應用程式的平均延遲。
相反,可以使用 io.Reader 和 io.Writer 介面來構建流式處理以限制每個請求使用的記憶體量。
為了提高效率,如果您使用大量的 io.Copy,請考慮實現 io.ReaderFrom / io.WriterTo。 這些介面效率更高,並且避免將記憶體複製到臨時緩衝區中。
6.6 超時,超時,超時
永遠不要開啟一個不知道耗費多少時間的 IO 操作。
你應該使用 SetDeadline,SetReadDeadline,SetWriteDeadline 函式給每一個網路請求設定超時機制。
6.6 Defer 函式代價很高,不是嗎?
從歷史上看,Defer 函式成本很高,因為它必須將 defer 函式的引數以閉包的形式儲存。
defer mu.Unlock()
等價於
defer func() {
mu.Unlock()
}()
如果完成的工作量很小,則 defer 會很昂貴,經典示例是將 struct 變數或對映查詢周圍的互斥鎖解鎖。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Go 高效能系列教程之二:效能評估和分析Go
- Go 高效能系列教程之五:記憶體和垃圾回收Go記憶體
- Go 高效能系列教程之一:基準測試Go
- Go 高效能系列教程之三:編譯器優化Go編譯優化
- Git最佳實踐建議Git
- 【建議收藏】swoft的最佳實踐
- 前端實習面試的一些建議前端面試
- Prompt進階系列4:LangGPT(構建高效能Prompt實踐指南)--結構化PromptGPT
- [譯] 使用 Go 和 ReactJS 構建聊天系統 (六)GoReactJS
- 圖靈社群改版的一些建議和思考圖靈
- Prompt進階2:LangGPT(構建高效能Prompt策略和技巧)--最佳實踐指南GPT
- 美團高效能終端實時日誌系統建設實踐
- Go 程式碼審查建議Go
- MySQL 高效能優化規範建議MySql優化
- kubernetes實踐之六:CFSSL構建本地CA
- 構建高效能和高彈性 WebSphere eXtreme Scale 應用程式的原則和最佳實踐WebREM
- 【SGA】RAC DB SGA超過100g的最佳實踐和建議
- 今日頭條Go建千億級微服務的實踐Go微服務
- Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- (1) Mysql高效能優化規範建議MySql優化
- JavaScript 實用技巧和寫法建議JavaScript
- 快手 RocketMQ 高效能實踐MQ
- 爬蟲程式實現過程中的一些建議爬蟲
- Tanenbaum對於構建產品,網路和生活的一些建議
- Oracle學習的一些建議Oracle
- Cursor AI應用一些建議AI
- 構建高效能 ASP.NET 應用的幾點建議ASP.NET
- 構建高效能 ASP.NET 應用的 12 點建議ASP.NET
- 構建高效能ASP.NET應用的幾點建議ASP.NET
- LLM 模型融合實踐指南:低成本構建高效能語言模型模型
- [譯] Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- jquery選擇器的實現流程簡析及提高效能建議!jQuery
- 使用 Go 和 ReactJS 構建聊天系統(六):Docker 化後端GoReactJSDocker後端
- 使用 Go 構建高效能檔案上傳器Go
- 編寫高效能Javascript程式碼的若干建議JavaScript
- EOS智慧合約的一些問題總結和建議
- golang開發:go併發的建議Golang
- 10個實用的Django技巧和建議Django