高质量编程与性能调优实践
项目目录:(使用的编译环境为Jetbrains Golang)
运行结果:(部分)
性能优化建议
- 简介:
1. 性能优化的前提是满足正确可靠、简洁清晰等质量因素。
2. 性能优化是综合评估,有时候 时间效率和空间效率可能对立。
3. 针对Go语言特性,介绍Go相关的性能优化建议。
- Benchmark
1. 性能表现需要实际数据衡量。
2. Go语言提供了支持基准性能测试的benchmark 工具。
Ø 例如:
结果:
-
BenchmarkFib10为测试函数名 -12表示GOMAXPROCS的值为12,也就是CPU核数为12。
-
4347187 表示一共执行1855870次,即b.N的值。
-
279.6 ns/op 表示每次执行花费279.6 ns/op。
-
每次执行申请多大的内存,每次执行申请几次内存应该都是0。
- Slice-预分配内存
1. 尽可能在使用make()初始化切片时提供容量信息。
2. 提前指定分配内存有利于降低执行时间和分配次数。
Ø 例如:
没有提前分配内存的执行时间:
提前分配了内存的执行时间:
- Slice的数据结构:
1. 切片本质是一个数组片段的描述,包括数组指针,片段的长度,片段的容量(不改变内存分配情况下的最大长度)。
2. 切片操作并不复制切片指向的元素。
3. 创建一个新的切片会复用原来切片的底层数组。
(由图可知上面部分的图表示提前分配内存;下面部分表示不提前分配内存时会有一个内存拷贝过程,这个过程需要时间)
(1) 若原来的切片比较大那么会出现大内存未释放的问题。
在已有切片基础上创建切片,不会创建新的底层数组。
(2) 使用场景:
原切片较大,代码在原切片基础上新建小切片。
原底层数组在内存中有引用,得不到释放可使用copy替代re-slice。
Ø 例如:
直接使用原始切片占用100.14MB,使用copy后占用3.14MB。
- MAP
分析
1. 不断向map中添加元素的操作会触发map的扩容
2. 提前分配好空间可以减少内存拷贝和 Rehash的消耗
3. 建议根据实际需求提前预估好需要的空间
- 字符串的处理
Ø 使用+的方式:
Ø 使用StrBuilder:
Ø 使用ByteBuffer:
结果:
结果表示:使用+拼接性能最差,strings.Builder, bytes. Buffer相近,strings. Buffer更快
- 分析
1. 字符串在Go语言中是不可变类型,占用内存大小是固定的
2. 使用+每次都会重新分配内存
3. strings.Builder, bytes. Buffer底层都是[]byte 数组
4. 内存扩容策略,不需要每次拼接重新分配内存
5. bytes.Buffer 转化为字符串时重新申请了一块空间
6. strings.Builder直接将底层的[]byte转换成了字符串类型返回
总结
在编写代码时不仅需要保证代码的正确性,而且要使用合适的工具和方法提高程序的性能,代码的正确性和代码的可维护性是同等重要的。