Golang笔记-基准测试

rundaisc · · 589 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

上一篇写了些简单的单元测试,这一篇来看看`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<b.N; i++{ JoinStrUseNor("aaa","bbb") } } func BenchmarkJoinStrUseSprint(b *testing.B) { b.ResetTimer() for i:=0; i<b.N; i++{ JoinStrUseSprint("aaa","bbb") } } ``` > 执行基础测试 ``` 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`。 `benchstat`是`Golang`官方推荐的一款命令行工具,可以针对一组或多组样本进行分析,如果同时分析两组样本(比如优化前和优化后),还可以给出性能变化结果。 > 安装 `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<b.N; i++{ JoinStrOpt("aaa","bbb") } } ``` - 执行测试并保存测试结果到`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`. >参考资料 - [go夜读] https://www.bilibili.com/video/BV1ae411x7dM - [benchstat] https://godoc.org/rsc.io/benchstat 期待一起交流![白色底可爱幼稚微信公众号底部二维码.png](https://static.studygolang.com/200521/caf053a764f06ff9c19fb351595983ca.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

589 次点击  
加入收藏 微博
上一篇:【go】context
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传