在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
文章转载地址:https://www.flysnow.org/2017/05/21/go-in-action-go-benchmark-test.html 什么是基准测试? 基准测试,是一种测试代码性能的方式,比如你有多种不同的方案,都可以解决问题,那么到底哪种方案性能 更好呢?这时候我们就可以使用基准测试 基准测试主要是通过测试 CPU 和 内存的效率,来评估被测试代码的性能,进而找到更好的解决方案 如何编写基准测试? 基准测试代码的编写和单元测试非常相似,它也有一定的规则,看如下示例: iota_test.go package main_test import ( "fmt" "testing" ) func BenchmarkSprintf(b *testing.B) { num := 10 b.ResetTimer() for i := 0; i < b.N; i++{ fmt.Sprintf("%d",num) } } ----------------------------------------------- 输出结果: goos: windows goarch: amd64 BenchmarkSprintf-8 20000000 93.0 ns/op PASS ok _/E_/GoProject/development/src 3.780s 规则如下: 1.基准测试的代码文件必须以 _test.go 结尾 2.基准测试的函数必须以 Benchmark 开头,必须是可以导出的 3.基准测试的函数必须接受一个指向 Benchmark 类型的指针作为唯一参数 4.基准测试函数不能有返回值 5.b.ResetTimer 是重置计时器,这样可以避免 for 循环之前的初始化代码干扰 6.最后的 for 循环很重要,被测试的代码要放到 for 循环里 7. b.N 是基准测试框架提供的,表示循环的次数,因为需要反复调用测试的代码,才可以评估性能 运行基准测试也要使用 go test 命令,不过我们需要加上 -bench= 标记,它接受一个表达式作为参数,匹配 基准测试的函数,. 表示运行所有基准测试 因为默认情况下,go test 会运行单元测试,为了防止单元测试的输出影响我们查看基准测的结果,我们可以 使用 -run 匹配一个从来没有的单元测试,过滤掉单元测试,我们这里使用 none,因为我们基本上不会创建这个名 字的单元测试方法 下面看一下输出结果的内容,我们可以看到在函数的后面有一个 -8 ,表示运行时对应的 GOMAXPROCS 的值。接着的 20000000 表示 for 循环的次数,也就是被测试代码调用的次数,最后的 93.0 ns/op 表示每次需要花费 93.0 纳秒 以上测试时间默认是 1s,也就是 1s 的时间被调用 20000000 次,每次花费 93.0 纳秒,如果想要测试运行的时间加长, 可以使用 -benchtime 指定,比如 3s,输出结果如下: goos: windows goarch: amd64 BenchmarkSprintf-8 50000000 97.5 ns/op PASS ok _/E_/GoProject/development/src 6.575s 可以看到,加长了测试运行时间,测试被调用的次数变大了,但是每次调用花费的时间变化不大,一般来说测试运行的时间 不要超过 3s 意义不大 性能对比 我们知道 Go 中将一个 int 类型转换成 string 类型常用的有三种方式,上面的基准测试是其中的一种,现在我们通过基准测试 来比较一下哪一种性能更高,如下示例: package main_test import ( "fmt" "strconv" "testing" ) // Sprintf 方式将 int 类型转换成 string 类型 func BenchmarkSprintf(b *testing.B) { num := 10 b.ResetTimer() for i := 0; i < b.N; i++ { fmt.Sprintf("%d",num) } } // Itoa 方式将 int 类型转换成 string类型 func BenchmarkItoa(b *testing.B) { num := 10 b.ResetTimer() for i := 0; i < b.N; i++ { strconv.Itoa(num) } } // 通过 FormatInt 方式将 int 类型转换成 string 类型 func BenchmarkFormatInt(b *testing.B) { num := 10 b.ResetTimer() for i := 0; i < b.N; i++ { strconv.FormatInt(int64(num),num) } } -------------------------------------------------------- 输出结果: goos: windows goarch: amd64 BenchmarkSprintf-8 20000000 96.3 ns/op BenchmarkItoa-8 300000000 4.13 ns/op BenchmarkFormatInt-8 1000000000 2.94 ns/op PASS ok _/E_/GoProject/development/src 7.487s 从运行结果上可以看到,strconv.FormatInt 函数最快,其次是 strconv.Itoa ,然后是 fmt.Sprintf 最慢,那么为什么 fmt.Sprintf 这么慢,现在我们通过 -benchmem 找一下具体原因,运行如下命令: go test -bench=. -benchmem -run=none ---------------------------------------------- 输出结果: goos: windows goarch: amd64 BenchmarkSprintf-8 20000000 95.4 ns/op 16 B/op 2 allocs/op BenchmarkItoa-8 300000000 4.17 ns/op 0 B/op 0 allocs/op BenchmarkFormatInt-8 1000000000 2.93 ns/op 0 B/op 0 allocs/op PASS ok _/E_/GoProject/development/src 7.396s -benchmem 可以提供每次操作分配内存的次数,以及每次操作分配的字节数,我们可以看到性能比较高的两个函数, 每次操作分配内存的次数都是 0,而最慢的那个每次操作分配内存的次数是 2;性能高的两个函数每次操作分配 0 字节内存, 最慢的那个每次操作分配 16 字节内存,从这个数据我们可以看出之所以慢的原因是内存分配占用太高
|
请发表评论