一些Golang性能优化 | 青训营笔记

306 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第一篇笔记

Slice性能优化

1、append

当 append 之后的长度小于等于 cap,将会直接利用原底层数组剩余的空间。
当 append 后的长度大于 cap 时,则会分配一块更大的区域来容纳新的底层数组。
因此,为了避免内存发生拷贝,如果能够知道最终的切片的大小,预先设置 cap 的值能够获得最好的性能。

2、delete

切片的底层是数组,因此删除意味着后面的元素需要逐个向前移位。每次删除的复杂度为 O(N),因此 切片不合适大量随机删除的场景。
删除后,将空余的位置置空,有助于垃圾回收。

3、性能陷阱

在已有切片的基础上进行切片,不会创建新的底层数组。因为原来的底层数组没有发生变化,内存会一直占用,直到没有变量引用该数组。比较推荐的做法,使用 copy 替代 re-slice

字符串拼接性能优化

使用 + 和 fmt.Sprintf 的效率是最低的。当然 fmt.Sprintf 通常是用来格式化字符串的,一般不会用来拼接字符串。

strings.Builderbytes.Buffer 和 []byte 的性能差距不大,消耗的内存也接近,综合易用性和性能,一般推荐使用 strings.Builder 来拼接字符串。

字符串在 Go 语言中是不可变类型,占用内存大小是固定的,当使用 + 拼接 2 个字符串时,生成一个新的字符串,那么就需要开辟一段新的空间,新空间的大小是原来两个字符串的大小之和。而 strings.Builderbytes.Buffer,包括切片 []byte 的内存是以倍数申请的,因此申请内存次数更少。

Map性能优化

1、预设容量

map是基于hash算法实现的,通过计算key的hash值来分布和查找对象,一般会通过拉链法解决冲突。如果初始容量太小,并且需要存入大量的数据,一定就会发生数据复制和rehash(go map 的负载因子是:6.5 )。所以预估容量就比较重要了,既能减少空间浪费,同时能避免运行时多次内存复制和rehash。 2、直接存储 对于小对象,直接将数据交由 map 保存,远比用指针高效。这不但减少了堆内存分配,关键还在于垃圾回收器不会扫描非指针类型 key/value 对象。