Golang笔记-基准测试

上一篇写了些简单的单元测试,这一篇来看看go中的基准测试。在go中基准测试也是开箱即用的。使用testing.B结构对象。

需要测试的代码

我们依然用上一篇的代码结构
│  main.go
│  main_test.go

main.go

func JoinStrUseSprint(a,b string) string {
    return  fmt.Sprintf("%s%s",a,b)
}

func JoinStrUseNor(a,b string) string {
    return  a+b
}

创建一个基准测试

创建普通单元测试我们使用 TestFunc来定义。创建基准测试我们需要使用 BenchmarkFunc来定义。
func TestJoinStrUseNor(t *testing.T) {
    s := JoinStrUseNor("aaa","bbb")
    t.Log(s)
}

func TestJoinStrUseSprint(t *testing.T) {
    s := JoinStrUseSprint("aaa","bbb")
    t.Log(s)
}

func BenchmarkJoinStrUseNor(b *testing.B) {
    b.ResetTimer()
    for i:=0; i
执行基础测试
go test -bench=. -benchtime=1s -benchmem -count=1
goos: linux
goarch: amd64
pkg: test
BenchmarkJoinStrUseNor-8        79888155                15.5 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrUseSprint-8      8956500               135 ns/op              40 B/op          3 allocs/op
PASS
ok      test    2.930s
命令参数
  • -bench=. 表示指定执行测试函数。.表示执行所有,如果修改为go test -bench=BenchmarkJoinStrUseSprint那么只会执行BenchmarkJoinStrUseSprint
  • -benchtime=1s指定执行时间为1s
  • -benchmem显示内存情况
  • -count=1表示执行一次
响应参数
  • goos: linux 操作系统
  • goarch: amd64 系统体系架构
  • BenchmarkJoinStrUseNor-8 执行的函数名称以及对应的GOMAXPROCS值。
  • 79888155 b.N的值
  • 15.5 ns/op 执行一次函数所花费的时间
  • 0 B/op 执行一次函数分配的内存
  • 0 allocs/op 执行一次函数所分配的内存次数
结果分析

通过上面的响应结果参数,我们很容易得出拼接两个字符串直接用+号连接,比使用fmt.Sprintf()的性能高。

样本分析

有时候因为一些因素的影响,我们单次测试的结果可能不准备,这个时候我们就需要进行多次测试比如go test -bench=BenchmarkJoinStrUseSprint -benchtime=1s -benchmem -count=10将测试的次数定义为10 或者更大,这个时候我就需要去分析这些结果得到相对正确的测试结果。但是这个时候时候如果我们人工去分析,工作量无疑是很大的,而且很困难。这个时候我们就可以借助一个工具 benchstat

benchstatGolang官方推荐的一款命令行工具,可以针对一组或多组样本进行分析,如果同时分析两组样本(比如优化前和优化后),还可以给出性能变化结果。

安装

go get golang.org/x/perf/cmd/benchstat

分析
  • 为了对比我们增加一个函数
func JoinStrOpt(a,b string) string {
    return  fmt.Sprintf("%s%s",a,b)
}
  • 增加测试
func BenchmarkJoinStrOpt(b *testing.B) {
    b.ResetTimer()
    for i:=0; i
  • 执行测试并保存测试结果到before.txt
go test -bench=BenchmarkJoinStrOpt -benchmem -count=10 | tee before.txt
goos: linux
goarch: amd64
pkg: test
BenchmarkJoinStrOpt-8            9143092               131 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9222475               131 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9344643               130 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9127231               131 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9223482               130 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9126334               131 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9364201               129 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9248034               130 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9034518               130 ns/op              40 B/op          3 allocs/op
BenchmarkJoinStrOpt-8            9102846               130 ns/op              40 B/op          3 allocs/op
PASS
ok      test    13.323s
  • 简单分析结果
benchstat before.txt
name          time/op
JoinStrOpt-8  130ns ± 1%

name          alloc/op
JoinStrOpt-8  40.0B ± 0%

name          allocs/op
JoinStrOpt-8   3.00 ± 0%

可以看出benchstrat会给我一个执行10测试的结果分析,平均数值以及波动数值。

对比分析
  • 修改代码为直接相加两个字符串既JoinStrUseNor的内容
func JoinStrOpt(a,b string) string {
    return  a+b
}
  • 再次执行测试并保存结果到after.txt
>go test -bench=BenchmarkJoinStrOpt -benchmem -count=10 | tee after.txt
goos: linux
goarch: amd64
pkg: test
BenchmarkJoinStrOpt-8           78033046                15.3 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           77211630                15.3 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           78088903                15.4 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           77907912                15.3 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           73805730                15.3 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           78508854                15.3 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           73493384                15.2 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           78618926                15.2 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           74973290                15.2 ns/op             0 B/op          0 allocs/op
BenchmarkJoinStrOpt-8           79287993                15.4 ns/op             0 B/op          0 allocs/op
PASS
ok      test    11.959s
  • 对比分析
>benchstat before.txt after.txt
name          old time/op    new time/op    delta
JoinStrOpt-8     130ns ± 1%      15ns ± 1%   -88.27%  (p=0.000 n=10+10)

name          old alloc/op   new alloc/op   delta
JoinStrOpt-8     40.0B ± 0%      0.0B       -100.00%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
JoinStrOpt-8      3.00 ± 0%      0.00       -100.00%  (p=0.000 n=10+10)

可以看到benchstat会给我直观的展示出优化代码前后的优化差值。

对比值括号中的数据含义

p表示结果的可信程度,p 值越大可信程度越低,统计学中通常把p=0.05做为临界值,超过此值说明结果不可信,可能是样本过少等原因。

n=10+10表示采用的样本数,,就跟投票一样通常会去掉一个最高分,一个最低分。出于某些原因(比如数据值反常,过大或过小),benchstat会舍弃某些样本,本例中优化前,优化后的数据没有舍弃所以是10+10.

参考资料

期待与您一起交流
白色底可爱幼稚微信公众号底部二维码.png

你可能感兴趣的:(golang,基准测试,benchmark)