在golang编程中,我们很方便的使用defer
来处理收尾工作。殊不知defer
也是要消耗一部份性能作为代价的。那么defer
到底对性能有多大影响,该怎么用就是值得考虑的问题了。
1. 性能的基准测试
基准测试入口函数,通过maxCount控制执行次数,deferFunc
和normalFunc
分别对应两种函数执行方式
//go:noinline
func doWork() {
// 实际执行函数
}
func BenchmarkDefer(b *testing.B) {
var maxCount = 1
for count := 1; count <= maxCount; count++ {
b.Run(strconv.Itoa(count), func(b *testing.B) {
b.Run("defer", func(b *testing.B) {
for i := 0; i < b.N; i++ {
deferFunc(count)
}
})
b.Run("normal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
normalFunc(count)
}
})
})
}
}
- 1.1 执行单个函数: 不使用任何控制流语句,直接执行
BenchmarkDefer的maxCount=1
//go:noinline
func deferFunc(i int) () {
defer doWork()
return
}
//go:noinline
func normalFunc(i int) () {
doWork()
return
}
// BenchmarkDefer()
测试结果: 单个执行,defer
相对normal
慢3.32倍
BenchmarkDefer
BenchmarkDefer/1
BenchmarkDefer/1/defer
BenchmarkDefer/1/defer-8 149949898 7.987 ns/op
BenchmarkDefer/1/normal
BenchmarkDefer/1/normal-8 538687286 2.401 ns/op
- 1.2 执行多个函数,使用控制流语句
BenchmarkDefer的maxCount=4
//go:noinline
func deferFunc(i int) (out int) {
switch i {
case 1:
defer doWork()
case 2:
defer doWork()
defer doWork()
case 3:
defer doWork()
defer doWork()
defer doWork()
case 4:
defer doWork()
defer doWork()
defer doWork()
defer doWork()
}
return
}
//go:noinline
func normalFunc(i int) (out int) {
switch i {
case 1:
doWork()
case 2:
doWork()
doWork()
case 3:
doWork()
doWork()
doWork()
case 4:
doWork()
doWork()
doWork()
doWork()
}
return
}
// BenchmarkDefer()
测试结果:
BenchmarkDefer
BenchmarkDefer/1
BenchmarkDefer/1/defer
BenchmarkDefer/1/defer-8 128937697 9.280 ns/op
BenchmarkDefer/1/normal
BenchmarkDefer/1/normal-8 337767033 3.493 ns/op
BenchmarkDefer/2
BenchmarkDefer/2/defer
BenchmarkDefer/2/defer-8 77090887 15.49 ns/op
BenchmarkDefer/2/normal
BenchmarkDefer/2/normal-8 270319077 4.434 ns/op
BenchmarkDefer/3
BenchmarkDefer/3/defer
BenchmarkDefer/3/defer-8 55449981 21.67 ns/op
BenchmarkDefer/3/normal
BenchmarkDefer/3/normal-8 251144298 4.746 ns/op
BenchmarkDefer/4
BenchmarkDefer/4/defer
BenchmarkDefer/4/defer-8 43583995 27.61 ns/op
BenchmarkDefer/4/normal
BenchmarkDefer/4/normal-8 211472344 5.686 ns/op
执行次数 | defer / normal 耗时比 |
---|---|
1 | 2.65 |
2 | 3.49 |
3 | 4.56 |
4 | 4.85 |
- 1.3 使用for循环执行指定次数
BenchmarkDefer的maxCount=4
//go:noinline
func deferFunc(i int) (out int) {
for ; i > 0; i-- {
defer doWork()
}
return
}
//go:noinline
func normalFunc(i int) () {
for ; i > 0; i-- {
doWork()
}
return
}
// BenchmarkDefer()
测试结果:
BenchmarkDefer
BenchmarkDefer/1
BenchmarkDefer/1/defer
BenchmarkDefer/1/defer-8 69172238 17.40 ns/op
BenchmarkDefer/1/normal
BenchmarkDefer/1/normal-8 250720538 4.544 ns/op
BenchmarkDefer/2
BenchmarkDefer/2/defer
BenchmarkDefer/2/defer-8 37119763 32.14 ns/op
BenchmarkDefer/2/normal
BenchmarkDefer/2/normal-8 182725000 6.011 ns/op
BenchmarkDefer/3
BenchmarkDefer/3/defer
BenchmarkDefer/3/defer-8 25962177 46.19 ns/op
BenchmarkDefer/3/normal
BenchmarkDefer/3/normal-8 158098609 7.662 ns/op
BenchmarkDefer/4
BenchmarkDefer/4/defer
BenchmarkDefer/4/defer-8 19954478 61.62 ns/op
BenchmarkDefer/4/normal
BenchmarkDefer/4/normal-8 130373722 9.233 ns/op
执行次数 | defer / normal 耗时比 |
---|---|
1 | 3.83 |
2 | 5.34 |
3 | 6.02 |
4 | 6.67 |
2. 结论
- 有多大影响
- defer相对normal执行,是有性能损耗的,单次执行,
defer/normal
为3.32
; - 执行次数越多,
defer/normal
耗时比越大; - 如果放在for循环中执行,则耗时比更大;
- defer相对normal执行,是有性能损耗的,单次执行,
- 使用建议
- 避免在for循环中使用defer;
- 对于执行频率较高的函数,避免使用defer;