这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
性能优化建议
Benchmark
性能表现需要实际数据衡量,Go语言提供了支持基准性能测试的benchmark工具
func Fib(n int) int {
if n < 2{
return n
}
return Fib(n-1) + Fib(n-2)
}
func BenchmarkFib10(b *testing.B) {
for n := 0; n < b.N; n++{
Fib(10)
}
}
运行benchmark:
go test -bench=. -benchmem
运行结果:
显示内容从上到下依次为,
BenchmarkFib10测试函数名
-8 CPU核数
4745695 执行的总次数
251.0 ns/op 每次CPU执行花费的时间
0B/op每次执行申请的内存
0 allocs/op 每次执行申请几次内存
slice
slice语句建议预分配内存,在使用make()初始化切片时提供容量信息
func NoPreAlloc(size int){
data:=make([]int, 0)
for k :=0; k< size; k++{
data = append(data, k)
}
}
func PreAlloc(size int){
data:=make([]int, 0, size)
for k:=0; k<size; k++{
data = append(data, k)
}
}
运行结果:
可以看出,预分配每次运行时间明显少于未预分配空间的方法,而且执行申请内存也减少。
产生差异的原因是切片是数组片段的描述,包括数组指针,片段长度和容量,切片操作不复制切片指向的元素,而创建新的切片复用原来的底层数组,所以在已有切片基础和是哪个创建切片,不会创建新的底层数组。而对于在已有切片基础上创建新切片场景,导致原底层数组在内存中有引用得不到释放,进而影响性能。此时可以用copy代替re-slice。
map
map预分配内存可以优化性能 ,其原因类似slice, 因为不断向map中添加元素会触发map扩容,而提前分配好空间可以有效减少内存拷贝和rehash的消耗。
字符串处理
常见字符串拼接方式有:直接用'+'连接; stringsBuilder;bytes.Buffer。对如上几种方式进行字符串拼接操作,列举案例如下:
func Plus(n int, str string) string{
s := " "
for i := 0; i < n; i++{
s+=str;
}
return s
}
func StrBuilder(n int, str string) string{
var builder strings.Builder
for i := 0; i < n; i++{
builder.WriteString(str)
}
return builder.String()
}
func ByteBuffer(n int, str string) string{
buf := new(bytes.Buffer)
for i := 0; i < n; i++{
builder.WriteString(str)
}
return builder.String()
}
运行结果:
由结果分析可得,使用strings.Builder性能和bytes.buffer接近,而且都比+快。原因如下:首先因为因为字符串在Go中占用内存大小固定,而+每次都会重新分配内存;而另外两个使用的底层是[]byte,有内存扩容策略,不需要每次拼接重新分配内存。
而byte.buffer转化为字符串时重新申请了空间,而string.Builder直接将底层[]byte转换成了字符串返回,所以会更快。
更进一步的性能优化建议是,在使用上述两种方法时也预先分配内存空间大小。
空结构体
空结构体struct{}实例不占据任何内存空间,可以作为占位符使用,能够节省资源而且具有很强的语义。例如只需要用map代替实现set,值设置为空结构体能够有效节省内存。
atomic包
一般锁的实现是通过操作系统调用来实现,而atomic操作是通过硬件实现,效率比锁搞,sync.Mutex应该用来保护一段逻辑而不是仅仅用于保护一个变量,对于非数值操作可以用atomic.Value来承载interface{}。
小结:避免常见性能陷阱可以保证大部分程序的性能,而且不要一味追求程序的性能,越高级的性能优化手段越容易出现问题。
性能分析工具
性能优化要依靠数据而不是猜测,用统一标准评估,服务优化要定位最大瓶颈而不是细枝末节,不要过早优化,因为产品在不断迭代优化,部分要求可能被更新,预期在即将出现性能问题后进行分析,不能过度优化,部分优化手段在修改需求后可能无法兼容。
性能分析工具pprof是能够可视化和简单性能分析的工具。基于github.com/wolfogre/go… 该项目埋入了炸弹代码,可以产生可观测的性能问题。性能分析主要针对CPU,堆内存,协程goroutine,锁Mutex,阻塞Block等方面进行展开。pprof提供可视化终端来分析代码性能。