自 2018 年以來,Go GC,以及更廣泛的 Go 運(yùn)行時(shí),一直在穩(wěn)步改進(jìn)。近日,Go 社區(qū)總結(jié)了 4 年來 Go 運(yùn)行時(shí)的一些重要變化。
這些重要變化主要是:
sync.Pool 是一種 GC 感知的重用內(nèi)存的工具,具有較低的延遲影響,并且能夠比之前更有效地回收內(nèi)存。(Go 1.13)
Go 運(yùn)行時(shí)能夠更主動(dòng)地將不需要的內(nèi)存返回給操作系統(tǒng),減少了內(nèi)存消耗和出現(xiàn)內(nèi)存不足的可能性。這將減少最高 20% 的空閑內(nèi)存消耗。(Go 1.13 和 1.14)
在許多情況下,Go 運(yùn)行時(shí)能夠更容易地?fù)屨?goroutine,最高可減少 90% 的 stop-the-world 延遲。(Go 1.14)
Go 運(yùn)行時(shí)能夠比以前更有效地管理計(jì)時(shí)器,特別是在擁有多核 CPU 的機(jī)器上。(Go 1.14)
在大多數(shù)情況下,現(xiàn)在使用 defer 語句的函數(shù)調(diào)用的開銷與常規(guī)函數(shù)調(diào)用一樣少。點(diǎn)擊這里觀看 Gophercon 2020 的相關(guān)演講。(Go 1.14)
內(nèi)存分配器的慢路徑對 CPU 核心的伸縮性更好,將吞吐量提升了最多 10%,并將尾部延遲降低了最多 30%,特別是在高度并行的程序中。(Go 1.14 和 1.15)
Go 內(nèi)存統(tǒng)計(jì)數(shù)據(jù)現(xiàn)在可以通過更細(xì)粒度、更靈活、更高效的 API(runtime/metrics 包)來訪問。這將獲取運(yùn)行時(shí)統(tǒng)計(jì)信息的延遲減少了兩個(gè)數(shù)量級(jí)(從毫秒到微秒)。(Go 1.16)
Go 調(diào)度器在尋找新任務(wù)時(shí)花費(fèi)的 CPU 時(shí)間減少了 30%。(Go 1.17)
Go 代碼現(xiàn)在在 amd64、arm64 和 ppc64 上遵循基于寄存器的調(diào)用約定,將 CPU 效率提升了最多 15%。(Go 1.17 和 1.18)
Go GC 的內(nèi)部審計(jì)和調(diào)度已經(jīng)進(jìn)行了重新設(shè)計(jì),解決了長期存在的各種與效率和健壯性相關(guān)的問題。對于 goroutine 占內(nèi)存使用很大一部分的應(yīng)用程序來說,這顯著降低了應(yīng)用程序的尾部延遲(最高達(dá) 66%)。(Go 1.18)
Go GC 現(xiàn)在在應(yīng)用程序空閑時(shí)會(huì)限制自己的 CPU 使用。這將空閑應(yīng)用程序的 GC 周期的 CPU 使用降低了 75%,從而減少可能導(dǎo)致作業(yè)調(diào)度器混淆的 CPU 峰值。(Go 1.19)
這些變化對用戶來說大多是看不見的——他們只需要升級(jí) Go,就可以看到他們所熟悉和喜愛的 Go 代碼運(yùn)行得更好了。
一個(gè)新的“旋鈕”Go 1.19 帶來了一個(gè)期待已久的特性,使用這個(gè)特性需要做一些額外的工作,但它具備很大的潛力:Go 運(yùn)行時(shí)的軟內(nèi)存限制。
多年來,Go GC 只有一個(gè)調(diào)優(yōu)參數(shù)——GOGC。GOGC 允許用戶在 CPU 開銷和內(nèi)存開銷之間做出權(quán)衡。多年來,這個(gè)“旋鈕”為 Go 社區(qū)提供了很好的服務(wù),被用在各種各樣的場景中。
Go 運(yùn)行時(shí)團(tuán)隊(duì)一直不愿意在 Go 運(yùn)行時(shí)中添加新的旋鈕,他們的理由很充分——每個(gè)新的旋鈕代表了配置空間中的一個(gè)新的維度,我們需要對其進(jìn)行測試和維護(hù),而且可能要永遠(yuǎn)持續(xù)下去。旋鈕的激增也給 Go 開發(fā)人員增加了理解和使用它們的負(fù)擔(dān),隨著旋鈕的增多,情況會(huì)變得愈加困難。因此,Go 運(yùn)行時(shí)總是傾向于用最小配置實(shí)現(xiàn)合理的行為。
那么為什么要添加內(nèi)存限制旋鈕呢?
內(nèi)存不像 CPU 時(shí)間那么具有可互換性。對于 CPU 時(shí)間,如果稍等片刻,將來總會(huì)得到更多的 CPU 時(shí)間。但對于內(nèi)存,你所擁有的總是有限的。
內(nèi)存限制解決了兩個(gè)問題。
首先,當(dāng)應(yīng)用程序的內(nèi)存使用峰值不可預(yù)測時(shí),僅靠 GOGC 幾乎無法防止內(nèi)存被耗盡。如果只使用 GOGC,Go 運(yùn)行時(shí)根本不知道它有多少可用的內(nèi)存。通過設(shè)置內(nèi)存限制,運(yùn)行時(shí)能夠意識(shí)到什么時(shí)候需要更努力地工作以減少內(nèi)存開銷,從而使運(yùn)行時(shí)能夠健壯地應(yīng)對瞬時(shí)的、可恢復(fù)的負(fù)載峰值。
第二是為了避免不使用內(nèi)存限制時(shí)出現(xiàn)的內(nèi)存不足。我們必須根據(jù)內(nèi)存峰值調(diào)優(yōu) GOGC,而為了保持較低的內(nèi)存開銷會(huì)導(dǎo)致更高的 GC CPU 開銷,即使應(yīng)用程序沒有處于內(nèi)存使用峰值且有足夠的可用內(nèi)存。這在容器化的環(huán)境中尤其重要。在容器化的環(huán)境中,程序被部署在具有獨(dú)立預(yù)留內(nèi)存的容器中。設(shè)置內(nèi)存限制可以為峰值負(fù)載提供保護(hù),并可以針對 CPU 開銷更主動(dòng)地調(diào)優(yōu) GOGC。
內(nèi)存限制的設(shè)計(jì)旨在易用性和健壯性。例如,它是對應(yīng)用程序中 Go 部分的整個(gè)內(nèi)存占用的限制,而不僅僅是 Go 的堆,因此用戶不需要額外計(jì)算 Go 運(yùn)行時(shí)的開銷。運(yùn)行時(shí)還會(huì)根據(jù)內(nèi)存限制調(diào)整其內(nèi)存清除策略,以便在內(nèi)存出現(xiàn)壓力時(shí)更主動(dòng)地將內(nèi)存返回給操作系統(tǒng)。
雖然內(nèi)存限制是一個(gè)強(qiáng)大的工具,但在使用時(shí)仍然要謹(jǐn)慎。其中一個(gè)需要注意的地方是,它會(huì)讓你的程序陷入 GC 抖動(dòng)狀態(tài)——在這種狀態(tài)下,程序運(yùn)行 GC 的時(shí)間過多,導(dǎo)致沒有足夠的時(shí)間來處理其他任務(wù)。例如,如果內(nèi)存限制設(shè)置得比程序?qū)嶋H需要的內(nèi)存少,Go 程序可能會(huì)崩潰。以前不太可能出現(xiàn) GC 抖動(dòng),除非顯式對 GOGC 進(jìn)行了大量調(diào)優(yōu)。我們選擇讓內(nèi)存耗盡而不是陷入抖動(dòng)狀態(tài),因此作為一種緩解措施,運(yùn)行時(shí)將 GC 限制為總 CPU 時(shí)間的 50%,即使這樣會(huì)超過內(nèi)存限制。
所有這些都需要慎重考慮,因此,作為這項(xiàng)工作的一部分,我們發(fā)布了一個(gè)新的 GC 指南,其中包含了交互式可視化的圖表,以幫助你們理解 GC 成本以及如何操作它們。
審核編輯:湯梓紅
-
cpu
+關(guān)注
關(guān)注
68文章
10854瀏覽量
211574 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3019瀏覽量
74003 -
Go
+關(guān)注
關(guān)注
0文章
43瀏覽量
12248
原文標(biāo)題:Go 運(yùn)行時(shí):4 年之后
文章出處:【微信號(hào):AI前線,微信公眾號(hào):AI前線】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
評(píng)論