在Go语言函数参数传递时,如果遇到数组传递或者结构体的传递,应尽量使用引用类型(指针),这样就可以避免内存复制,也就用不着GC对新复制的内存进行回收了。
package gc_friendly
import (
"testing"
)
const NumOfElems = 1000
type Content struct {
Detail [10000]int
}
func withValue(arr [NumOfElems]Content) int {
// fmt.Println(&arr[2])
return 0
}
func withReference(arr *[NumOfElems]Content) int {
//b := *arr
// fmt.Println(&arr[2])
return 0
}
func TestFn(t *testing.T) {
var arr [NumOfElems]Content
//fmt.Println(&arr[2])
withValue(arr)
withReference(&arr)
}
func BenchmarkPassingArrayWithValue(b *testing.B) {
var arr [NumOfElems]Content
b.ResetTimer()
for i := 0; i < b.N; i++ {
withValue(arr)
}
b.StopTimer()
}
func BenchmarkPassingArrayWithRef(b *testing.B) {
var arr [NumOfElems]Content
b.ResetTimer()
for i := 0; i < b.N; i++ {
withReference(&arr)
}
b.StopTimer()
}
withValue函数中,使用了值传递方式传递参数,即不管入参内存有多大,都会再复制一份,将复制的内存传递给withValue函数。
withReference函数使用的是引用传递,即仅将入参的内存地址传递给函数,不会对整块内存进行复制。
使用-bench参数运行这段测试代码,查看两种方式的性能差别:
$ go test -v -bench=.
=== RUN TestFn
--- PASS: TestFn (0.08s)
goos: windows
goarch: amd64
pkg: gc_friendly
BenchmarkPassingArrayWithValue-4 50 21839360 ns/op
BenchmarkPassingArrayWithRef-4 1000000000 0.332 ns/op
PASS
ok gc_friendly 2.590s
可以看到,两种不同的参数传递方式的性能差距非常大。
使用了引用类型进行参数传递的方法仅用了0.332 ns/op。
除了使用上面Benchmark分析性能方法,我们还可以通过打印GC日志来对比不同参数传递方式的性能差异。
BenchmarkPassingArrayWithValue函数分析:
$ GODEBUG=gctrace=1 go test -bench=BenchmarkPassingArrayWithValue
scvg: 0 MB released
scvg: inuse: 3, idle: 4, sys: 7, released: 3, consumed: 4 (MB)
scvg: 0 MB released
scvg: inuse: 2, idle: 5, sys: 7, released: 3, consumed: 4 (MB)
scvg: inuse: 2, idle: 5, sys: 7, released: 3, consumed: 4 (MB)
scvg: 0 MB released
......
gc 123 @3.118s 0%: 0+14+0 ms clock, 0+0/14/0+0 ms cpu, 152->152->0 MB, 153 MB goal, 4 P
scvg: inuse: 153, idle: 250, sys: 403, released: 186, consumed: 217 (MB)
48 26047596 ns/op
PASS
ok gc_friendly 3.481s
通过GC日志末尾的输出可以看到,函数执行了48次,GC执行了123次。
BenchmarkPassingArrayWithRef函数分析:
$ GODEBUG=gctrace=1 go test -bench=BenchmarkPassingArrayWithRef
scvg: 0 MB released
scvg: inuse: 0, idle: 163, sys: 163, released: 13, consumed: 150 (MB)
gc 15 @0.390s 0%: 0+0+0 ms clock, 0+0/0/0+0 ms cpu, 76->76->0 MB, 77 MB goal, 4 P
scvg: 0 MB released
scvg: inuse: 0, idle: 163, sys: 163, released: 13, consumed: 150 (MB)
1000000000 0.464 ns/op
PASS
ok gc_friendly 0.972s
函数执行了1000000000次,GC回收内存次数为15。
博客内容为极客时间视频课《Go语言从入门到实战》学习笔记。
参考课程链接:
https://time.geekbang.org/course/intro/160?code=NHxez89MnqwIfa%2FvqTiTIaYof1kxYhaEs6o2kf3ZxhU%3D&utm_term=SPoster
博客参考代码:
https://github.com/geektime-geekbang/go_learning/tree/master/code/ch49/gc_friendly