Go语言学习笔记(1)--- 基准测试
go 刘宇帅 3年前 阅读量: 626
什么是基准测试
基准测试是测量一个程序在固定工作负载下的性能。我们通常会用来对比对同一个问题不同解决方案的性能,从而帮助我们做决定选择哪个方案更合理。
Go 的基准测试
Go 基准测试规则如下:
- 必须放在以 _test.go 结尾的文件中。
- 函数名必须以 Benchmark 开头
- 函数必须接受一个参数 *testing.B
- 没有返回值
基准测试受机器状态等因素的影响每次跑的结果很难保持完全一致,但是基本会在一个很小的范围内波动。
写一个简单的基准测试如下
import "testing"
func sum() int {
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
return sum
}
func BenchmarkTest(b *testing.B) {
for i := 0; i < b.N; i++ {
sum()
}
}
运行结果如下
> $ go test -bench=Sum -run=^1
goos: darwin
goarch: amd64
pkg: github.com/yushuailiu/easyGolang/benchmark
BenchmarkSum-4 20000000 60.8 ns/op
PASS
ok github.com/yushuailiu/easyGolang/benchmark 1.287s
跑基准测试仍然使用 go test 命令,但是需要添加 -bench= 参数来标识跑基准测试,-bench 参数值跟一个正则表达式表示需要跑的基准测试,我们可以用 . 表示跑所有的,如果我们基准测试比较多可以使用正则指明,比如上面的我们只需要跑 Sum。-run=^1 是为了禁止跑单元测试,因为 go test 默认会在跑基准测试前跑单元测试,我们这里为了单纯的跑基准测试,所以需要禁止单元测试,而 -run=^1 用来指明需要跑的单元测试的正则,很显然没有以 1 开头的的单元测试,所有该参数的意思是禁止跑单元测试。
输出结果我们可以看到 BenchmarkSum-4,这里是 4 是指运行时的 GOMAXPROC 的值。 20000000 表示 t.N 的值,即我们跑了 20000000 次 sum(),60.8 表示跑一次需要 60.8 纳秒。
基准测试参数-benchtime= 用来指定基准测试的时间,默认基准测试是跑1s,有时候性能相当的两个程序可以通过增加压测时间对比差别,但是最长不要超过3s,因为如果算法是稳定的时间再长是没有意义的。
基准测试参数-benchmem用来显示每次操作内存分配的次数,以及每次操作分配的字节数,可以用来对比不同程序的性能根源,因为很显然内存操作多,分配字节多的自然会需要更多地时间。
基准测试的具体使用
我前面有写了一篇关于冒泡排序算法的实现,里面有提到 3 种实现,我们这里对 3 种实现做基准测试比较 3 种实现的性能。基准测试代码源码地址 算法地址
import "testing"
var list = []map[string][]int{
{
"origin": {10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
"result": {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
{
"origin": {22, 33, 22, 1, 2, 11, 22, 123, 110},
"result": {1, 2, 11, 22, 22, 22, 33, 110, 123},
},
{
"origin": {22, 23, 10, 9, 54, 17, 100, 2},
"result": {2, 9, 10, 17, 22, 23, 54, 100},
},
{
"origin": {22, 23, 22, 23, 22, 23, 22, 23},
"result": {22, 22, 22, 22, 23, 23, 23, 23},
},
{
"origin": {1, 1111, 298989, 8422, 2222, 44422},
"result": {1, 1111, 2222, 8422, 44422, 298989},
},
{
"origin": {22, 10, 1, 99, 22, 10, 11, 10, 2, 100},
"result": {1, 2, 10, 10, 10, 11, 22, 22, 99, 100},
},
{
"origin": {23, 22},
"result": {22, 23},
},
{
"origin": {1},
"result": {1},
},
{
"origin": {},
"result": {},
},
}
func listEqual(list1, list2 []int) bool {
for index, _ := range list1 {
if list1[index] != list2[index] {
return false
}
}
return true
}
func BenchmarkBubbleSort1(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, item := range list {
origin := make([]int, len(item["origin"]))
copy(origin, item["origin"])
BubbleSort1(origin)
if !listEqual(origin, item["result"]) {
b.Error("bubble sort", item["origin"], "should be", item["result"],
"but get", origin)
}
}
}
}
func BenchmarkBubbleSort2(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, item := range list {
origin := make([]int, len(item["origin"]))
copy(origin, item["origin"])
BubbleSort2(origin)
if !listEqual(origin, item["result"]) {
b.Error("bubble sort", item["origin"], "should be", item["result"],
"but get", origin)
}
}
}
}
func BenchmarkBubbleSort3(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, item := range list {
origin := make([]int, len(item["origin"]))
copy(origin, item["origin"])
BubbleSort3(origin)
if !listEqual(origin, item["result"]) {
b.Error("bubble sort", item["origin"], "should be", item["result"],
"but get", origin)
}
}
}
}
运行基准测试
> $ go test -bench=BubbleSort* -run=^1 -v
goos: darwin
goarch: amd64
pkg: github.com/yushuailiu/go-algorithm/sort
BenchmarkBubbleSort1-4 1000000 1012 ns/op
BenchmarkBubbleSort2-4 2000000 993 ns/op
BenchmarkBubbleSort3-4 2000000 883 ns/op
PASS
ok github.com/yushuailiu/go-algorithm/sort 6.676s
可以看到 BubbleSort2 比 BubbleSort1 稍有提升,而 BubbleSort3 比 BubbleSort2 提升还是挺大的。