一個 8bytes 的內存優化

最近看 Dave Cheney 的一篇文章 [1],發現一個有趣的代碼片段,裏面提到了一個 8byte 的內存優化。

代碼片段是這樣的:

func BenchmarkSortStrings(b *testing.B) {
 s := []string{"heart""lungs""brain""kidneys""pancreas"}
 b.ReportAllocs()
 for i := 0; i < b.N; i++ {
  var ss sort.StringSlice = s
  var si sort.Interface = ss
  sort.Sort(si)
 }
}

代碼很簡單,是對一個[]string做排序的內存分配壓測

其中

var ss sort.StringSlice = s
var si sort.Interface = ss
sort.Sort(si)

等同於代碼

sort.Strings(s)

涉及了對 []string 轉換爲實現排序的接口 sort.Interface

瞭解iface實現的同學知道其data會存儲底層數據,一般是一個機器字長的大小(8bytes

slice24bytes: 底層數組指針(8bytes)+ 長度(8bytes)+ 容量(8bytes

直接存不下,就只能indirection, 存指向切片的指針了

創建前片指針的過程中,切片 escape 到了 heap 上,因爲不知道原切片ss是否會在si使用過程中消失。

即,內存分配發生在了這裏!

看下壓測結果(go1.16):

$ go test sort_test.go -bench . -benchmem
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
BenchmarkInts-4         18846020                90.09 ns/op           24 B/op          1 allocs/op
PASS
ok      command-line-arguments  2.663s

結果就是24bytes啊,那所謂的8bytes優化在哪裏呢

是相對於的go1.16前版本的go的, 比如go1.15下:

$ go1.15 test sort_test.go -bench . -benchmem
goos: darwin
goarch: amd64
BenchmarkSortStrings-4           8617881               172 ns/op              32 B/op          1 allocs/op
PASS
ok      command-line-arguments  1.649s

找了下具體提交,其實就是在內存分配裏針對三個機器字大小增加了24bytessizeClass,避免原來的向上取整到32bytes

感興趣的同學可以去看看具體實現:runtime: add 24 byte allocation size class[2]

題外話,好久沒有更新了,不好意思了都,後邊再撿起來 😂

參考資料

[1]

Dave Cheney 的一篇文章: https://dave.cheney.net/2021/01/05/a-few-bytes-here-a-few-there-pretty-soon-youre-talking-real-memor

[2]

runtime: add 24 byte allocation size class: https://github.com/golang/go/commit/14c7caae5074fdf0d97a3ad995e20c63e4065cbf

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