go性能优化 | 青训营笔记

87 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

今天和大家分享提高 Go 编程性能的相关知识点

性能优化

性能基准测试

go本身提供了性能基准测试框架 Benchmark,它可以利用反复调用,来实现性能测试效果,具体使用在前面的测试笔记中有提到。

slice优化

  1. 提前设定容量

    我们应该尽可能的在使用 make() 初始化切片的时候提供容量信息:

    arr := make([]int, 0, 20)
    

    因为当切片容量不够的时候,go内部会进行 ×2 的扩容操作,这里会有划分地址和内存的操作,为了不影响性能,我们应该在使用之前就设定好容量值。

  2. 及时释放大内存切片

    在已有切片的基础上创建切片,不会创建新的底层数组,此时如果原切片很大,其内存就得不到即时的释放。此时我们可以使用copy函数创建新的底层数组。

    arr := originArr[98:100] // 不会去创建新底层数组,originArr底层的整个数组依然会保持引用状态// 优化写法
    arr := make([]int, 2)
    copy(arr, originArr[98:100])
    

map优化

  1. 提前设定容量(不断添加元素会触发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包的原子操作只能保护一个变量,但同时他是利用硬件实现的,所以他的性能很高。