转自:Debugging performance issues in Go programs

1. 将多个小对象合并成一个大对象

2. 减少不必要的指针简介引用,多使用 copy 引用

例如使用 bytes.Buffer 代替 *bytes.Buffer, 因为使用指针时会分配两个对象来完成引用

3. 局部变量逃逸时,将其聚合起来

这一点跟理论1相同,核心在于减少 object 的分配,减少 gc 压力。例如,如下代码:

1
2
3
4
5
6
for k, v := range m {
    k, v := k, v  // copy for capturing by the goroutine
    go func() {
        // use k and v
    }()
}

可以优化为:优化后,逃逸对象变为了 x, 将 k,v 两个对象减少为一个对象

1
2
3
4
5
6
for k, v := range m {
    x := struct{k, v string}{k, v} // copy for capturing by the goroutine
    go func() {
        // use x.k and x.v
    }()
}

4. []byte 的预分配

当我们比较清楚的知道 []byte 到底会有多少个字节时,我们就可以采用一个数组来分配这段内存,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type x struct {
    buf     []byte
    bufArr  [16]byte // bufArr usually does not growth beyond 16 bytes
}

func MakeX() *X {
    x := &X{}
    // preinitialize buf with the backing array
    x.buf = x.buffArr[:0]
    return x
}

5. 尽可能使用字节数少的类型

当我们的一些 const 或着计数器字段不需要太大的字节数时,我们通常可以将其声明为 int8 类型

6. 减少不必要的指针引用

当一个对象不包含任何指针(注意:strings,slices,maps 和chans包含隐含的指针),时,对gc的扫描影响很小。 比如,1GB byte 的slice事实上只包含有限的几个object,不会影响垃圾收集时间。 因此,我们可以尽可能的减少指针的引用。

7. 使用 sync.Pool 来缓存常用的对象

See Also

Thanks to the authors 🙂