sync.Pool 的使用場景
一句話總結:儲存和複用臨時對象,減少記憶體配置設定,降低 GC 壓力。舉個簡單的例子:
type Student struct {
Name string
Age int32
Remark [1024]byte
}
var buf, _ = json.Marshal(Student{Name: "Geektutu", Age: 25})
func unmarsh() {
stu := &Student{}
json.Unmarshal(buf, stu)
}
json 的反序列化在文本解析和網絡通信過程中非常常見,當程式并發度非常高的情況下,短時間内需要建立大量的臨時對象。而這些對象是都是配置設定在堆上的,會給 GC 造成很大壓力,嚴重影響程式的性能。
Go 語言從 1.3 版本開始提供了對象重用的機制,即 sync.Pool。sync.Pool 是可伸縮的,同時也是并發安全的,其大小僅受限于記憶體的大小。sync.Pool 用于存儲那些被配置設定了但是沒有被使用,而未來可能會使用的值。這樣就可以不用再次經過記憶體配置設定,可直接複用已有對象,減輕 GC 的壓力,進而提升系統的性能。sync.Pool 的大小是可伸縮的,高負載時會動态擴容,存放在池中的對象如果不活躍了會被自動清理。
如何使用
sync.Pool 的使用方式非常簡單:
聲明對象池
隻需要實作 New 函數即可。對象池中沒有對象時,将會調用 New 函數建立。
var studentPool = sync.Pool{
New: func() interface{} {
return new(Student)
},
}
Get & Put
Get() 用于從對象池中擷取對象,因為傳回值是 interface{},是以需要類型轉換。
Put() 則是在對象使用完畢後,傳回對象池。
stu := studentPool.Get().(*Student)
json.Unmarshal(buf, stu)
studentPool.Put(stu)
性能測試
struct 反序列化
func BenchmarkUnmarshal(b *testing.B) {
for n := 0; n < b.N; n++ {
stu := &Student{}
json.Unmarshal(buf, stu)
}
}
func BenchmarkUnmarshalWithPool(b *testing.B) {
for n := 0; n < b.N; n++ {
stu := studentPool.Get().(*Student)
json.Unmarshal(buf, stu)
studentPool.Put(stu)
}
}
測試結果如下:
$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/hpg-sync-pool
BenchmarkUnmarshal-8 1993 559768 ns/op 5096 B/op 7 allocs/op
BenchmarkUnmarshalWithPool-8 1976 550223 ns/op 234 B/op 6 allocs/op
PASS
ok example/hpg-sync-pool 2.334s