前言
一个满足需求能顺利跑起来的项目是合格的,但仅仅只是跑起来是不够的。一个优秀的项目除了满足基本的需求,还要有良好的性能。倘若响应一个事件都要等待许久,会让客户放弃你的软件,转而去投向性能更出色的软件。
在一些性能敏感的事务上,因为一时间会有海量数据处理,差1ms也会造成庞大的性能浪费。
除了时间上的效率,空间效率也是一部分。优秀的空间性能亦能节省许多的存储费用。但时间性能和空间性能往往是此消彼长的。但目前存储设备价格降低,时间效率的提高比空间效率的提高性价比更高.
Go语言基准性能测试
性能的提高并不是靠猜测,也不是使用了什么技巧就会得到性能的提升。而是要依靠工具测量。只有实际数据才能准确表示性能高低。
基准性能测试:benchmark工具
使用方法:
- 在后缀"_test.go"的测试文件里写测试函数
- 基准测试函数名需要以 BenchmarkXxx(b *testing.B) 命名
- go test 命令
go test -bench=. -benchmem
下面是一个简单的例子:
fib_test.go:
package goTest
import "testing"
func Fib(n int)int{
if n<2{
return n
}
return Fib(n-1)+Fib(n-2)
}
func BenchmarkFib10(b *testing.B){
for n:=0;n<b.N;n++{
Fib(10)
}
}
goos: linux
goarch: amd64
pkg: goTest
cpu: ************
=== RUN BenchmarkFib10
BenchmarkFib10
BenchmarkFib10-16 5247408 230.0 ns/op 0 B/op 0 allocs/op
PASS
ok goTest 1.444s
倒数第四行分别是:
- 测试的函数名和GOMAXPROCS的值(16:线程数)
- 表示执行的次数————b.N的值
- 每次执行花费
- 每次执行申请内存
- 每次执行申请几次内存
根据基准性能测试可以更好的定位程序的性能问题,发现痛点,解决痛点。不得不说go test的工具链十分充足。
性能优化建议
Slice 切片
切片预分配
在使用make初始化切片时,提供容量信息。
因为切片的本质就是一串记录了信息的数组,而数组的拓展需要复制之前的数据和开辟新的内存空间。内存拷贝开销巨大极度浪费性能,所以尽可能提前分配好。让内存尽可能提前分配,减少运行中分配,减少性能损耗。
使用copy替代re-slice
在原来庞大的切片上创建一个小切片时,相当于在原本的切片上引用,未创建新的切片空间。gc不会将原切片清除掉。
而使用copy则会复制那一段切片,这样在原切片失去引用时也会被gc掉。
Map 预分配内存
向map添加元素会造成拓容,理由与切片一样,会造成性能浪费。提前预估分配好map空间,有利于减少内存拷贝开销。
字符串处理
使用strings.Builder / strings.Buffer
尽可能不使用 string + string 这样会造成内存分配开销,如果多次相加的话。 使用BUilder或BUffer会预分配内存,无需每次字符拼接都重新分配内存。