性能调优"/>
Go学习:pprof性能调优
调优前
代码
package main/**
无重复字符的最长字串;支持中文
*/
func lengthOfNonRepratingSubStr(s string) int {lastOccurred := make(map[rune]int)start := 0maxLen := 0for i, ch := range []rune(s) {if lastIndex, ok := lastOccurred[ch]; ok && lastIndex >= start {start = lastIndex + 1}if i-start+1 > maxLen {maxLen = i - start + 1}lastOccurred[ch] = i}return maxLen
}
性能测试
func BenchmarkSubStr(b *testing.B) {param := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"for i := 0; i < 13; i++ {param = param + param}b.Logf("len(param) = %d",len(param))for i := 0; i < b.N; i++ {val := lengthOfNonRepratingSubStr(param)ans := 8if val != ans {b.Errorf("lengtyum install graphvizhOfNonRepratingSubStr(%s); 期望值:%d; 实际值: %d", param, ans, val)}}
}
没有进行调优时,进行性能测试12920060 ns/op;不算特别慢;但是还可以更快!
Graphviz
使用Graphviz和pprof命令分析代码现在的问题;然后有针对性的去优化代码
Graphviz介绍
Graphviz是贝尔实验室开发的一个开源的工具包,它使用一个特定的DSL(领域特定语言):dot作为脚本语言,然后使用布局引擎来解析此脚本,并完成自动布局.
Graphviz安装
Graphviz本身是一个绘图工具软件,下载点击地址.如果你是linux,可以用api-get或者yum的方法安装.如果是windows,就在官网下载msi文件安装;无论是linux还是windows,装完后都要设置环境变量.
调优
分析代码
- 使用到的命令
go test -bench .
go test -bench . -cpuprofile cpu.out
go tool pprof cpu.out
web
优化思路
- 我们通过命令+Graphviz生成出一张图;这张图的方框有大有小;线有粗有细,方框越大消耗的时间越长,线越粗消耗的时间越长
- 通过对图片的分析
- mapaccess2_fast32 0.38s (18.18%) of 0.62s (29.67%)
- mapassign_fast32 0.39s (18.66%) of 0.57s (27.27%)
- stringtoslicerune 0.20s (9.57%) of 0.49s (23.44%)
- 主要是map和把参数转成[]rune消耗比较大;但是我们有utf8字符串传进来,解码是必不可少的,就只能在map上进行优化
优化代码
使用数组代替map;用空间换时间.map里面每个rune塞进去,它要算哈希;要判重;要分配空间;这一系列操作都太慢了;我们使用数组一开始就把空间分配好;使用空间来换时间
func lengthOfNonRepratingSubStr(s string) int {lastOccurred := make([]int, 0xffff)for i := range lastOccurred {lastOccurred[i] = -1}start := 0maxLen := 0for i, ch := range []rune(s) {if lastIndex := lastOccurred[ch]; lastIndex != -1 && lastIndex >= start {start = lastIndex + 1}if i-start+1 > maxLen {maxLen = i - start + 1}lastOccurred[ch] = i}return maxLen
}
初步优化,进行性能测试4562497 ns/op;性能提高的非常明显,肉眼可见
再次进行分析
通过对图片进行分析
- stringtoslicerune 0.35s (16.91%) of 1.17s (56.52%);解码必不可少
- asyncPreempt 0.02s (0.97%); asyncPreempt异步无需优化
- makeslice 0 of 0.11s (5.31%); 创建数组花费的时间还可以继续优化;
优化代码
我们把创建数组的动作提到函数外面,把创建数组的动作减少到一次
var lastOccurred = make([]int, 0xffff)func lengthOfNonRepratingSubStr(s string) int {for i := range lastOccurred {lastOccurred[i] = -1}start := 0maxLen := 0for i, ch := range []rune(s) {if lastIndex := lastOccurred[ch]; lastIndex != -1 && lastIndex >= start {start = lastIndex + 1}if i-start+1 > maxLen {maxLen = i - start + 1}lastOccurred[ch] = i}return maxLen
}
优化后,进行性能测试4140482 ns/op;相比上次性能测试的结果,这次更快了!
再次进行分析
现在对图片进行分析就只剩一个必不可少的解码动作比较耗时,其他的都已经完成了优化!
总结
- 每次进行优化都要先获取性能数据
- 然后查看性能数据
- 再分析慢在哪里
- 优化代码
- 最后再获取性能数组;循环往复,直到把代码优化到一个比较满意的地步
总之,调优不是一蹴而就的,需要分析,推理,实践,总结,再分析,最终定位到具体的问题
更多推荐
Go学习:pprof性能调优
发布评论