Go 编码建议——性能篇
Posted 恋喵大鲤鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 编码建议——性能篇相关的知识,希望对你有一定的参考价值。
性能方面的建议只适用于高频场景。如果执行的频率较低,稍差的性能代码一般情况下也是可以接受的。
1.优先使用 strconv 而不是 fmt
将原语转换为字符串或从字符串转换时,strconv 速度比 fmt 快。
// Bad
for i := 0; i < b.N; i++
s := fmt.Sprint(rand.Int())
BenchmarkFmtSprint-4 143 ns/op 2 allocs/op
// Good
for i := 0; i < b.N; i++
s := strconv.Itoa(rand.Int())
BenchmarkStrconv-4 64.2 ns/op 1 allocs/op
为什么性能上会有两倍多的差距,因为 fmt 实现上利用反射来达到范型的效果,在运行时进行类型判断,所以带来了一定的性能损耗。
2.避免字符串到字节的转换
不要反复从固定字符串创建字节 slice。相反,请执行一次转换并捕获结果。
// Bad
for i := 0; i < b.N; i++
w.Write([]byte("Hello world"))
BenchmarkBad-4 50000000 22.2 ns/op
// Good
data := []byte("Hello world")
for i := 0; i < b.N; i++
w.Write(data)
BenchmarkGood-4 500000000 3.25 ns/op
3.指定容器容量
尽可能指定容器容量,以便为容器预先分配内存。这将在后续添加元素时减少通过复制来调整容器大小。
3.1 指定 map 容量提示
在尽可能的情况下,在使用 make() 初始化的时候提供容量信息。
make(map[T1]T2, hint)
向 make() 提供容量提示会在初始化时尝试调整 map 的大小,这将减少在将元素添加到 map 时为 map 重新分配内存。
注意,与 slice 不同。map capacity 提示并不保证完全的抢占式分配,而是用于估计所需的 hashmap bucket 的数量。 因此,在将元素添加到 map 时,甚至在指定 map 容量时,仍可能发生分配。
// Bad
m := make(map[string]os.FileInfo)
files, _ := ioutil.ReadDir("./files")
for _, f := range files
m[f.Name()] = f
// m 是在没有大小提示的情况下创建的; 在运行时可能会有更多分配。
// Good
files, _ := ioutil.ReadDir("./files")
m := make(map[string]os.FileInfo, len(files))
for _, f := range files
m[f.Name()] = f
// m 是有大小提示创建的;在运行时可能会有更少的分配。
3.2 指定切片容量
在尽可能的情况下,在使用 make() 初始化切片时提供容量信息,特别是在追加切片时。
make([]T, length, capacity)
与 map 不同,slice capacity 不是一个提示:编译器将为提供给 make() 的 slice 的容量分配足够的内存,这意味着后续的 append() 操作将导致零分配(直到 slice 的长度与容量匹配,在此之后,任何 append 都可能调整大小以容纳其他元素)。
const size = 1000000
// Bad
for n := 0; n < b.N; n++
data := make([]int, 0)
for k := 0; k < size; k++
data = append(data, k)
BenchmarkBad-4 219 5202179 ns/op
// Good
for n := 0; n < b.N; n++
data := make([]int, 0, size)
for k := 0; k < size; k++
data = append(data, k)
BenchmarkGood-4 706 1528934 ns/op
参考文献
以上是关于Go 编码建议——性能篇的主要内容,如果未能解决你的问题,请参考以下文章