这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
在有了高质量代码的之后,如何在大规模程序部署的场景下,尽可能的提升性能,节省资源成本就是我们接下来要思考的问题了。本文将记录一些关于性能优化的建议。
性能测试工具-Benchmark
测试结果中包括:
- 测试函数名-CPU核数
- 单位时间内被测函数运行的次数
- 每次运行的耗时
- 每次执行申请的内存大小
- 每次执行申请内存的次数
Slice性能优化
- slice预分配内存
尽可能在使用make()初始化切片时提供容量信息,特别是在追加切片时。若没有进行预分配内存,那么内存拷贝等过程将降低性能。
- 大内存的释放
若直接在大切片的基础上进行切片,会导致虽然使用的只有一小段但底层数组在内存中仍能占据大量空间无法释放。因此建议使用copy代替re-slice,使得有新的小的底层数组建立,不再被引用的大内存能够得到释放。
Map性能优化
- map预分配内存
与slice预分配内存同理,不断向map中添加元素会触发map扩容,因此提前分配好空间可以减少内存拷贝和Rehash的消耗,提前分配的空间大小建议根据实际需求提前进行预估。
字符串处理
- 使用strings.Builder
常用的字符串处理方法有:+拼接,strings.Builder,bytes.Buffer。比较三种处理方法发现,+拼接的性能最差,strings.Builder和bytes.Buffer在性能上相近,但strings.Builder更快。分析原因,字符串在Go语言中是不可变类型,占用内存大小固定,在使用‘+’拼接时就会涉及到内存分配,而strings.Builder和bytes.Buffer底层都是[]byte数组,有相应的内存策略,不需要每次拼接都重新分配内存。另外,在bytes.Buffer转字符串时会申请新空间存字符串,而strings.Builder是直接在底层[]byte进行转换,所以建议使用strings.Builder。
空结构体
- 使用空结构体节省内存
空结构体实例不占据任何的内存空间,可作为各种场景下的占位符使用
atomic包
- 使用atomic包实现互斥
实现互斥的方法除atomic包外,还可以使用锁来实现,但锁是通过操作系统来实现的,相较于
通过硬件来实现的atomic来说,锁的效率较低。
总结
首先,性能优化的前提是要保证程序的质量,不能一味地追求性能;其次,我发现减少内存的分配时提高性能常用的有效的方法,无论是预分配内存还是字符串处理都是通过降低内存分配次数提高了性能;最后,希望我能在之后的编码中避免掉入性能陷阱,通过这节课所学的知识来提高自己的程序质量和性能。