newsence

在堆疊上進行記憶體配置

Hacker News·大約 1 個月前

我們一直在尋找讓 Go 程式更快的方法,並在 1.25 與 1.26 版本中透過編譯器優化,將原先在堆積上的切片配置轉移到堆疊上,藉此減輕垃圾回收器的負擔並提升執行效率。

背景

這篇文章由 Go 團隊的 Keith Randall 撰寫,探討了 Go 語言在近期版本中如何透過編譯器優化,將原本需要在堆積(Heap)上分配的記憶體轉移到堆疊(Stack)上。這項改進主要針對 Slice 的動態擴展過程,透過在 Go 1.25 與 1.26 中引入的「投機性堆疊分配」技術,減少垃圾回收器的負擔並提升快取友善性,進而讓開發者在不改變既有程式碼邏輯的情況下獲得效能提升。

社群觀點

針對 Go 語言這項自動化優化,Hacker News 的討論主要圍繞在不同程式語言對堆疊分配的處理哲學,以及這種優化在實務上的潛在風險。部分開發者聯想到 C 與 C++ 中歷史悠久的 alloca() 函式,指出在堆疊上分配變動長度空間並非新概念,其優勢在於僅需移動堆疊指標即可完成分配,速度極快且生命週期隨函式結束而終止。然而,社群也對此提出警示,認為過度依賴堆疊分配可能導致堆疊溢位(Stack Overflow),特別是在遞迴呼叫或處理來自使用者輸入的變動長度資料時,這類操作往往被視為潛在的安全漏洞。

在實作層面上,有評論者質疑這種優化是否真的能帶來全面性的效能提升。一種觀點認為,過大的堆疊框架會破壞快取的局部性,因為堆疊本質上與堆積共享相同的實體記憶體,若堆疊占用過大,反而可能降低快取效率。此外,也有人討論到 Go 語言目前的執行環境限制,例如 Go 採用的是連續堆疊並在空間不足時進行搬移,這與某些系統透過延遲映射大範圍虛擬記憶體來擴展堆疊的做法不同,因此 Go 必須在編譯時期進行更謹慎的靜態分析或投機分配。

對於追求極致效能的開發者而言,社群中也提出了一些替代方案。例如使用 Arena 分配器來管理具有相同生命週期的物件,或是利用執行緒區域緩衝區(Thread-local buffer)來規避頻繁的分配動作。針對 Go 語言的未來發展,有討論提到是否能結合剖析導向優化(PGO)來讓編譯器更精準地預測堆疊分配的大小,從而平衡記憶體浪費與分配效率。整體而言,雖然部分開發者認為這類優化像是某種程度的補丁,但大多數人仍肯定這種能讓既有模式自動變快的底層改進。

延伸閱讀

https://go.dev/blog/allocation-optimizations