Go:一个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
)
而slice
是24bytes
: 底层数组指针(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
找了下具体提交,其实就是在内存分配里针对三个机器字大小增加了24bytes
的sizeClass
,避免原来的向上取整到32bytes
感兴趣的同学可以去看看具体实现:runtime: add 24 byte allocation size class[2]
题外话,好久没有更新了,不好意思了都,后边再捡起来 😂
参考资料
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
推荐阅读