这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天
今天和大家分享提高 Go 编程性能的相关知识点
性能优化
性能基准测试
go本身提供了性能基准测试框架 Benchmark,它可以利用反复调用,来实现性能测试效果,具体使用在前面的测试笔记中有提到。
slice优化
-
提前设定容量
我们应该尽可能的在使用
make()初始化切片的时候提供容量信息:arr := make([]int, 0, 20)因为当切片容量不够的时候,go内部会进行
×2的扩容操作,这里会有划分地址和内存的操作,为了不影响性能,我们应该在使用之前就设定好容量值。 -
及时释放大内存切片
在已有切片的基础上创建切片,不会创建新的底层数组,此时如果原切片很大,其内存就得不到即时的释放。此时我们可以使用copy函数创建新的底层数组。
arr := originArr[98:100] // 不会去创建新底层数组,originArr底层的整个数组依然会保持引用状态 // 优化写法 arr := make([]int, 2) copy(arr, originArr[98:100])
map优化
-
提前设定容量(不断添加元素会触发map扩容)
data := make(map[int]string, 100)
strings.Builder
对常见的字符串操作,我们可以使用 strings.Builder 来提升性能
var builder strings.Builder
for i := 0; i < 2; i++ {
builder.WriteString("123")
}
// builder.String() 为 123123
为什么 strings.Builder 会性能更好呢?因为其内部是使用 []Byte 数组实现的,而普通的字符串在 Go 语言中属于不可变类型(内存大小都固定),其每次进行 + 操作来拼接字符串都会重新分配内存。相比之下, strings.Builder 则会利用扩容策略来避免每次都需要重新分配内存。
atomic
"sync/atomic"可以实现原子操作,适合在并发时使用,并且比常用的加锁操作更加节省开销和更好性能:
type Num struct {
i int32
}
func Add() {
num := new(Num)
atomic.AddInt32(&num.i, 1)
}
atomic包的原子操作只能保护一个变量,但同时他是利用硬件实现的,所以他的性能很高。