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
- 一些建議,送給備考六西格瑪的你!
- 前端實習面試的一些建議前端面試
- kubernetes實踐之六:CFSSL構建本地CA
- [譯] 使用 Go 和 ReactJS 構建聊天系統 (六)GoReactJS
- Prompt進階2:LangGPT(構建高效能Prompt策略和技巧)--最佳實踐指南GPT
- MySQL 高效能優化規範建議MySql優化
- Go 程式碼審查建議Go
- 美團高效能終端實時日誌系統建設實踐
- 爬蟲程式實現過程中的一些建議爬蟲
- 今日頭條Go建千億級微服務的實踐Go微服務
- (1) Mysql高效能優化規範建議MySql優化
- Cursor AI應用一些建議AI
- 使用 Go 和 ReactJS 構建聊天系統(六):Docker 化後端GoReactJSDocker後端
- 快手 RocketMQ 高效能實踐MQ
- Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- EOS智慧合約的一些問題總結和建議
- ElasticSearch實戰系列六: Logstash快速入門和實戰Elasticsearch
- LLM 模型融合實踐指南:低成本構建高效能語言模型模型
- TensorFlow 下構建高效能神經網路模型的最佳實踐神經網路模型
- Python教程之udp和tcp協議介紹PythonUDPTCP協議
- [譯] Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- 一些有價值的工作建議
- 頁面優化的一些建議優化
- 一些運維相關的建議運維
- 一些好的職業建議文章
- 關於學習的一些建議
- 使用 Go 構建高效能檔案上傳器Go
- 【Go】string 優化誤區及建議Go優化
- golang開發:go併發的建議Golang
- Docker 實戰教程之從入門到提高 (六)Docker
- gitlab上CI/CD的一些小實踐和理解Gitlab