Go1-20 實驗:內存 Arenas VS 傳統內存管理

注意

Go arenas 是一個實驗性功能。API 和實現完全不受支持,Go 團隊不保證兼容性,也不保證在任何未來版本中繼續存在。

請通過這個 github 討論區 [1] 來了解更多信息。

簡介

Go 1.20 引入了一個實驗性的內存管理概念 “arenas”,可以用來提高 Go 程序的性能。在本博客文章中,我們將探討:

什麼是內存 Arenas

Go 語言是一種利用垃圾回收機制的編程語言,這意味着運行時會自動幫助程序員管理內存的分配和釋放。這消除了手動內存管理的需求,但也帶來了代價:

Go 運行時必須跟蹤 * 每個 * 分配的對象,導致性能開銷增加。

在某些情形下,例如 HTTP 服務器處理具有大量 protobuf blob(其中包含許多小對象)的請求時,Go 運行時可能會花費大量時間跟蹤每個分配,然後釋放它們。因此,這也導致了明顯的性能開銷。

Arenas 提供了一種解決這個問題的方法,通過減少與許多小分配相關的開銷。在這個 protobuf blob 示例中,可以在解析之前分配一大塊內存(Arenas),以便所有已解析的對象可以放置在 arenas 內並作爲一個整體單元進行跟蹤。

一旦解析完成,整個 arenas 可以一次性釋放,進一步減少釋放許多小對象的開銷。

判斷可以從 arenas 中受益的代碼

任何分配大量小對象的代碼都有可能從 arenas 中受益。但是如何知道代碼分配的過多?根據我們的經驗,最好的方法是對程序進行分析。

使用 Pyroscope,我們可以獲得其中一個雲服務 [2] 的分配配置文件(alloc_objects)。

你可以看到內存分配(533.30 M)的大部分來自代碼的一個區域 - 這是在底部調用函數InsertStackA的紫色節點。鑑於它代表 65%的分配,這是使用 arenas 的好候選者。但是,通過減少這些分配是否可以獲得足夠的性能收益?讓我們看看同一服務的 CPU 分析(cpu):

幾件事情很突出:

因此,理論上,如果我們優化了這個程序中的所有分配,我們可以減少 14%+5%= 19%的 CPU 時間。這將轉化爲我們所有客戶的 19%成本節約和延遲改進。在實踐中,不太可能真正使這些數字降到零,但這仍然是應用程序執行的重要工作,可能值得優化。

我們做出的優化

如果您對此感興趣,您可以在 Pyroscope 存儲庫中找到公共拉取請求 [3] 作爲參考。

我們 Arenas 實驗的結論

上面的火焰圖表示我們實施更改後的配置文件。您可以看到,許多runtime.mallocgc調用現在已經消失,但被 arenas 特定的等效項(runtime.(*userArena).alloc)替代,您也可以看到垃圾回收開銷減少了一半。僅從火焰圖上看準確的節省量很難看出,但是當我們查看結合了火焰圖和 AWS 指標的 CPU 使用情況的 Grafana 儀表板時,我們發現 CPU 使用率大約減少了 8%。這直接轉化爲該特定服務的雲賬單上的 8%費用節省,使其成爲一項有價值的改進。

這可能看起來不多,但重要的是要注意,這是一項已經被優化得相當多的服務。例如,我們使用的 Protobuf 解析器根本不會分配任何額外的內存,垃圾回收開銷(5%)也在我們服務的開銷範圍的低端。我們認爲代碼庫的其他部分還有很多改進的空間,因此我們很高興繼續嘗試 arenas 。

權衡弊端

雖然 arenas 可以提供性能上的好處,但在使用它們之前有必要考慮利弊。使用 arenas 的主要缺點是,一旦使用 arenas,您現在必須手動管理內存,如果您不小心,這可能導致嚴重問題:

以下是我們的建議:

Go arenas 目前的一個主要缺點是它是一項實驗性特性。API 和實現是完全不受支持的,Go 團隊不對其兼容性或將來是否會繼續存在進行任何保證。我們建議您將所有與 arenas 相關的代碼提取到單獨的包中,並使用 build tags 以確保如果您決定停止使用 arenas,它很容易從您的代碼庫中刪除。我們的代碼 [9] 可作爲此方法的演示。

解決社區關注的問題

Go 團隊已經收到了關於 arenas 的大量反饋,我們想要回應社區中我們所看到的一些擔憂。有關 arenas 最常見的問題是它們添加了一種隱式且不立即顯現問題的程序崩潰方式,使語言變得更加複雜。

大部分的批評是明確的但誤導性的。我們不預期 arenas 會變得普遍。我們認爲 arenas 是一個強大的工具,但只適用於特定情況。在我們看來, arenas 應該包含在標準庫中,但它們的使用應該受到警惕,就像使用unsafereflectcgo一樣。

我們對 arenas 的經驗非常充分,我們能夠證明 arenas 可以顯着減少垃圾回收和內存分配的時間。本文描述的實驗關注的是一個單獨的、已經高度優化的服務,我們仍然能夠通過使用 arenas 獲得 8% 的額外性能。我們認爲許多用戶可以從在代碼庫中使用 arenas 中獲益更多。

此外,我們還發現,相比我們過去嘗試的其他優化(如使用緩衝池或編寫自定義無分配 protobuf 解析器), arenas 的實現更容易。與其他類型的優化相比,它們具有相同的缺點,但提供了更多的好處 - 因此,在我們看來, arenas 是一個淨贏。我們希望未來能看到 arenas 成爲標準庫的一部分(並且是常用包如 protobuf 或 JSON 解析器的一部分)。

總結

Arenas 對於優化 Go 程序是一個強大的工具,特別適用於處理大量 protobuf 或 JSON 塊的情況。它們有可能帶來顯著的性能改進,但是需要注意的是它們是一個實驗性的功能,不保證兼容性或在未來版本中的存在。

我們建議您對應用程序進行分析,並在代碼庫的有限部分嘗試使用 arenas,並將您的結果報告給 Go 團隊 [10]。

相關鏈接:

[1]https://github.com/golang/go/issues/51317#issuecomment-1385623024

[2]https://pyroscope.io/pricing/

[3]https://github.com/pyroscope-io/pyroscope/pull/1804

[4]https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-70ab4bbe796a97ad1a47d7970504296eff36b5307527ae2806d2b50f94f83a45

[5]https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-32bf8c53a15c8a5f7eb424b21c8502dc4905ec3caa28fac50f64277361ae746fR417

[6]https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-34edf37e55842273380ee6cb31c9245f31ed25aa6d7898b0f2c25145f17d8ea0R170

[7]https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-abe15b6d3634170650f86bb7283aa15265de2197cffa969deda2dd5b26fcecd9R89-R92

[8]https://pyroscope.io/pricing

[9]https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-70ab4bbe796a97ad1a47d7970504296eff36b5307527ae2806d2b50f94f83a45

[10]https://github.com/golang/go/issues/51317

原文地址:

Go 1.20 Experiment: Memory Arenas vs Traditional Memory Management | Open Source Continuous Profiling Platform (pyroscope.io)

原文作者:

Dmitry Filimonov

本文永久鏈接: translator/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md at master · gocn/translator (github.com)

譯者:zxmfke

校對:cvley

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/1-744datNkVoTpvqaUaUQA