Go 部落格

Go 執行時:4 年後

Michael Knyszek
2022 年 9 月 26 日

自 2018 年我們釋出上一篇關於 Go GC 的部落格文章以來,Go GC 以及更廣泛的 Go 執行時一直在穩步改進。我們解決了一些大型專案,這些專案源於真實的 Go 程式以及 Go 使用者面臨的實際挑戰。讓我們向您介紹一下亮點!

有什麼新特性?

  • sync.Pool 是一個 GC 感知的記憶體複用工具,與之前相比,它對延遲的影響更小,並且更有效地回收記憶體。(Go 1.13)

  • Go 執行時更主動地將不需要的記憶體返回給作業系統,從而減少過多的記憶體消耗和記憶體不足錯誤的機率。這使空閒記憶體消耗降低高達 20%。(Go 1.13 和 1.14)

  • 在許多情況下,Go 執行時能夠更輕鬆地搶佔 goroutines,從而將 stop-the-world 延遲降低高達 90%。觀看此處 Gophercon 2020 的演講。 (Go 1.14)

  • 與之前相比,Go 執行時更有效地管理計時器,尤其是在具有許多 CPU 核心的機器上。(Go 1.14)

  • 在大多數情況下,使用 defer 語句延遲的函式呼叫現在與普通函式呼叫的開銷一樣小。觀看此處 Gophercon 2020 的演講。 (Go 1.14)

  • 記憶體分配器的慢路徑與 CPU 核心擴充套件性更好,尤其是在高度並行的程式中,吞吐量增加高達 10%,尾部延遲減少高達 30%。(Go 1.14 和 1.15)

  • Go 記憶體統計資訊現在可透過更精細、靈活和高效的 API 訪問,即 runtime/metrics 包。這將獲取執行時統計資訊的延遲降低了兩個數量級(毫秒級到微秒級)。(Go 1.16)

  • Go 排程器用於查詢新工作的自旋 CPU 時間減少高達 30%。(Go 1.17)

  • Go 程式碼現在在 amd64、arm64 和 ppc64 上遵循基於暫存器的呼叫約定,將 CPU 效率提高高達 15%。(Go 1.17 和 Go 1.18)

  • Go GC 的內部記賬和排程已重新設計,解決了與效率和魯棒性相關的一系列長期存在的問題。對於 goroutines 棧佔用記憶體比例較高的應用程式,這顯著降低了應用程式的尾部延遲(高達 66%)。(Go 1.18)

  • Go GC 現在在應用程式空閒時限制自身的 CPU 使用率。這使得在非常空閒的應用程式中,GC 週期期間的 CPU 利用率降低了 75%,減少了可能干擾工作分配器的 CPU 峰值。(Go 1.19)

這些變化對使用者來說大多是不可見的:他們熟悉和喜愛的 Go 程式碼只需升級 Go 就能執行得更好。

一個新的調節旋鈕

Go 1.19 帶來了一個長期以來備受期待的特性,它需要一些額外的操作才能使用,但潛力巨大:Go 執行時的軟記憶體限制

多年來,Go GC 只有一個調優引數:GOGCGOGC 允許使用者調整Go GC 在 CPU 開銷和記憶體開銷之間進行的權衡。多年來,這個“旋鈕”很好地服務了 Go 社群,涵蓋了各種各樣的用例。

Go 執行時團隊一直不願在 Go 執行時中新增新的調節旋鈕,這是有充分理由的:每個新的旋鈕都代表了配置空間中一個新的維度,我們需要測試和維護它,這可能永遠持續下去。旋鈕的增多也會給 Go 開發者帶來負擔,使他們更難理解和有效使用這些旋鈕。因此,Go 執行時一直傾向於在最小配置下表現合理。

那麼為什麼還要新增一個記憶體限制旋鈕呢?

記憶體不像 CPU 時間那樣易於替代。對於 CPU 時間,只要稍等片刻,將來總會有更多。但對於記憶體,您擁有的數量是有限的。

記憶體限制解決了兩個問題。

首先,當應用程式的峰值記憶體使用量不可預測時,僅憑 GOGC 幾乎無法防止記憶體耗盡。僅使用 GOGC 時,Go 執行時根本不知道它有多少可用記憶體。設定記憶體限制使執行時能夠透過感知何時需要更努力地減少記憶體開銷來應對瞬態、可恢復的負載峰值,從而提高魯棒性。

其次,為了在不使用記憶體限制的情況下避免記憶體不足錯誤,必須根據峰值記憶體來調整 GOGC,即使應用程式未處於峰值記憶體使用狀態且有大量可用記憶體,這也會導致更高的 GC CPU 開銷以維持較低的記憶體開銷。這在我們容器化的世界中尤為重要,其中程式被放置在具有特定和隔離記憶體預留的容器中;我們不妨利用這些預留!透過提供針對負載峰值的保護,設定記憶體限制可以使 GOGC 在 CPU 開銷方面進行更激進的調優。

記憶體限制旨在易於採用且魯棒。例如,它限制的是應用程式中 Go 部分的整體記憶體佔用,而不僅僅是 Go 堆,因此使用者無需擔心計算 Go 執行時開銷。執行時還會根據記憶體限制調整其記憶體回收策略,以便在面臨記憶體壓力時更主動地將記憶體返回給作業系統。

但是,儘管記憶體限制是一個強大的工具,但仍需謹慎使用。一個重要的注意事項是,它可能導致程式出現 GC 抖動(GC thrashing):即程式花費過多時間執行 GC,導致沒有足夠的時間進行有意義的進度。例如,如果記憶體限制設定得低於程式實際需要的記憶體量,則 Go 程式可能會發生抖動。GC 抖動在之前不太可能發生,除非明確將 GOGC 調整得非常偏向於記憶體使用。我們選擇寧願記憶體耗盡也不願出現抖動,因此作為緩解措施,執行時會將 GC 限制在總 CPU 時間的 50%以內,即使這意味著超出記憶體限制。

所有這些都需要仔細考慮,因此作為這項工作的一部分,我們釋出了一份全新的 GC 指南,其中包含互動式視覺化,幫助您瞭解 GC 開銷以及如何對其進行操作。

結論

試試記憶體限制!在生產環境中使用它!閱讀GC 指南

我們一直在尋求關於如何改進 Go 的反饋,但聽到它對您有效也非常有幫助。向我們傳送反饋

下一篇文章:Go 十三年
上一篇文章:Go 開發者調查 2022 年第二季度結果
部落格索引