这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
性能优化
使用Benchmark进行性能分析
使用规则
1、文件名必须以_test.go结尾。
2、基准测试的函数必须以Benchmark开头。
3、基准测试函数必须接受一 个指向Benchmark类型的指针作为唯一参数。
4、该函数不能有返回值。
5、被测试的代码应该放入在for循环中。
使用go test -bench=. -benchmem开始测试
性能优化建议
slice预分配内存
尽可能在使用make()初始化切片提供容量信息
data:=make([]int,0,size) 提供size容量信息进行预分配
原因:切片本质是一个数组片段的描述
包括数组指针,片段的长度len,片段的容量cap(片段预分配的最大长度)
切片操作并不赋值切片指向的元素,而是创建一个心的切片并复用切片的底层数组
以append为例,append时有两种场景:
当append之后的长度小于等于cap,将会直接利用原底层数组剩余的空间
当append后的长度大于cap时,则会分配一块更大的区域来容纳心的底层数组
当我们在已有切片基础上创建切片,不会创建新的底层数组,但是原底层输在在内存中有引用,得不到释放,这时候我们可以使用copy替代re-slice,append,通过copy,指向一个新的底层数组,原有的大内存数组会被垃圾回收
map预分配内存
跟slice类似,不断向map中添加元素的操作会触发map的扩容,提前分配浩空间可以减少内存拷贝和Rchash的消耗
字符串处理
使用strings.Builder处理字符串替代+
性能最高的是strings.Buffer 其次是strings.Builder,bytes.Buffer 最慢的是+
原因:字符串在Go语言中是不可变类型,占用内存大小是固定的 使用+每次都会重新分配内存
其中strings.Buffer转化为字符串重新申请了一块空间 strings.Builder直接将字节数组转换成了字符串类型,除此之外,我们也可以使用预分配的思想提升字符串处理的速度
空结构体
使用空结构体可以节省内存,空结构体的实例不占据任何的内存空间,可作为占位符使用 例子:利用set的实现,只需要key的使用,不需要value
type Set map[string]struct{}
func (s Set) Append(k string) {
s[k] = struct{}{}//使用空结构体作为占位符
}
func (s Set) Remove(k string) {
delete(s, k)
}
func (s Set) Exist(k string) bool {
_, ok := s[k]
return ok
}
func main() {
set := Set{}
set.Append("煎鱼")
set.Append("咸鱼")
fmt.Println(set.Exist("煎鱼"))
}
使用atomic包
锁的实现是通过操作系统来实现,atomic操作通过硬件实现,效率比锁高 sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量 对于非数值操作,可以使用atomic.Value
性能分析工具:pprof
需安装Graphviz插件 指令:go tool pprof"http://localhost:6060/debug/profile?seconds=10" 使用-http=:8080可以在浏览器中打开pprof 分析协程:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/heap" 堆内存:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/goroutine" 锁:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/mutex" 阻塞: go tool pprof -http=:8080"http://localhost:6060/debug/pprof/block"