Go arena 民間庫來了,可以手動管理內存!
大家好,我是煎魚。
上年我們有討論過關於 Go arena 手動管理內存的相關提案。一開始還高歌猛進,但沒想到後面由於嚴重的 API 問題(想把 arena 應用到其他的標準庫中,但會引入大問題):
Go 核心團隊中途咕咕咕到現在,沒有新的推動和突破性進展,實屬尷尬。
最近有社區的大佬有了新的動作,來自 Grafana 的 @Miguel Ángel Ortuño 開源了一個新的第三方庫 ortuman/nuke[1],用於完成 arena 手動管理內存的訴求。
今天我們基於官方資料此進行使用分享和介紹,也好未雨綢繆一下。
溫習前置知識
Arena 指的是一種從一個連續的內存區域分配一組內存對象的方式。當然了,它的重點是要手動管理內存,實現一些編程上的內存管理目標。
優點比一般的內存分配更有效率,也可以一次性釋放。缺點上需要程序員在編程時手動管理,有可能會泄漏和錯釋放,增大了心智負擔。
簡單來講就是,Arena 可以手動管理內存,可以做很多事,有利有弊。也 “容易” 崩。
快速介紹
安裝
安裝命令如下:
go get -u github.com/ortuman/nuke
需要注意這個庫要求 go >= 1.21.7,在實際下載前建議先進行升級。
使用案例
常規使用
基本使用該 arean 庫的用法,代碼如下:
import (
"github.com/ortuman/nuke"
)
type Foo struct{ A int }
func main() {
arena := nuke.NewMonotonicArena(256*1024, 80)
fooRef := nuke.New[Foo](arena "Foo")
fooSlice := nuke.MakeSlice[Foo](arena, 0, 10 "Foo")
for i := 0; i < 20; i++ {
fooSlice = nuke.SliceAppend(arena, fooSlice, Foo{A: i})
}
// 做一些煎魚的業務邏輯...
arena.Reset(true)
...
}
-
初始化一個新的 arean 內存區域,緩衝區大小爲 256KB,最大內存上限爲 20MB。
-
聲明和分配一個 Foo 類型的新對象和容量爲 10 個元素的 Foo 切片。
-
業務邏輯完成後,重置所申請的 arean 內存區域(釋放)。
以上是最常用的方式,相當於在某一塊代碼片段中進行初始化和處理。
基於 context 場景
如果我們需要在 HTTP 請求這類整個生命週期中去使用。
可以藉助 context,使用如下方式:
func httpHandler(w http.ResponseWriter, r *http.Request) {
arena := nuke.NewMonotonicArena(64*1024, 10)
defer arena.Reset(true)
ctx := nuke.InjectContextArena(r.Context(), arena)
processRequest(ctx)
// 給煎魚靜悄悄乾點什麼...
}
func processRequest(ctx context.Context) {
arena := nuke.ExtractContextArena(ctx)
// ...
}
func main() {
http.HandleFunc("/", httpHandler) fmt.Println("Server is listening on port 8080...")
http.ListenAndServe(":8080", nil)
}
在請求端 http context 中注入 arena,再在實際處理的地方通過 context 獲取 arena,以此達到穿越整體生命週期的方式。
基於併發場景
默認場景下,nuke.NewMonotonicArena
初始化出來的 arena,有一個隱性的坑,他不是併發安全的!
大膽猜測,這是基於性能的考慮,所以沒有做到一起。畢竟鎖會很喫資源。而在 Go 裏要去做手動內存管理的場景,多少又對性能有一定的訴求。
在有併發訴求的場景下,可以使用 NewConcurrentArena
函數:
import (
"github.com/ortuman/nuke"
)
func main() {
arena := nuke.NewConcurrentArena(
nuke.NewMonotonicArena(256*1024, 20),
)
defer arena.Reset(true)
// 和煎魚煎個魚看看...
}
除了換了個初始化方法,其他用法與常規用法差不多。
也看了下官方的 Benchmarks,確實是基於性能考慮的區分併發與非併發的業務場景。QPS 越大,性能差距越大:
BenchmarkMonotonicArenaNewObject/100-8 124495 15469 ns/op 0 B/op 0 allocs/op
BenchmarkMonotonicArenaNewObject/1000-8 76744 19602 ns/op 0 B/op 0 allocs/op
BenchmarkMonotonicArenaNewObject/10000-8 24104 50845 ns/op 0 B/op 0 allocs/op
BenchmarkMonotonicArenaNewObject/100000-8 3282 366044 ns/op 0 B/op 0 allocs/op
BenchmarkConcurrentMonotonicArenaNewObject/100-8 90392 16679 ns/op 0 B/op 0 allocs/op
BenchmarkConcurrentMonotonicArenaNewObject/1000-8 43753 29823 ns/op 0 B/op 0 allocs/op
BenchmarkConcurrentMonotonicArenaNewObject/10000-8 8037 149923 ns/op 0 B/op 0 allocs/op
BenchmarkConcurrentMonotonicArenaNewObject/100000-8 879 1364377 ns/op
總結
今天給大家分享了 Go 官方 arena 的最新進展和情況,主體上還是由於嚴重 API 原因(擔憂像 context 一樣造成傳染性)沒有突破性進展。雖然有人提出可以放到 unsafe 庫中,也獲得了許多人表情點贊。但仍然沒能打動 Go 核心團隊的同學。
基於此,我們介紹了民間大佬的 arena 開源庫 ortuman/nuke。基本功能和使用都能夠滿足需求。後續有此類業務需求時,可以隨時拿起來就用!
參考資料
[1]
ortuman/nuke: https://github.com/ortuman/nuke
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aS-azhReZL5epoG_3n0hyg