基准测试可以用来识别某段代码的 CPU 或者内存效率问题。基准测试可以被用来测试不同的并发模式或者被用来辅助配置工作池的数量,以保证能最大化系统的吞吐量。
package chapter10
import (
"fmt"
"testing"
)
// BenchmarkSprintf 对 fmt.Sprintf 函数进行基准测试
func BenchmarkSprintf(b *testing.B) {
number := 10
b.ResetTimer()
for i := 0; i < b.N; i++ {
fmt.Sprintf("%d", number)
}
}
Go 的基准测试文件名也必须以 _test.go 结尾。同时也必须导入 testing 包。基准测试函数必须以 Bechmark 开头,接收一个指向 testing.B 类型的指针作为唯一参数。为了让基准测试框架能准确测试性能,它必须在一段时间内反复运行这段代码,所以这里使用了 for 循环。
基准测试框架默认会在持续1秒的时间内,反复调用需要测试的函数。测试框架每次调用测试函数时,都会增加 b.N 的值。第一次调用时,b.N 的值为1。需要注意的是,一定要将所有要进行基准测试的代码放在循环里,并且循环要使用 b.N 的值。否则,测试的结果是不可靠的。
其执行命令为:
go test -v -run=“none” -bench=“BenchmarkSprintf”
其运行结果为:
在 go test 调用中,我们给 -run 选项传递了字符串"none",来保证在运行置顶的基准测试函数之前没有单元测试会被运行。
在上面的运行结果中,第一个数字 10000000 表示在循环中的代码被执行的次数。在这个例子里,一共执行了 10000000 次。之后的数字表示代码的性能,单位为每次操作消耗的纳秒(ns)数。其表示使用 Sprintf 函数平均每次花费了 119 纳秒。
从结果中,我们可以看到这个测试框架持续运行了大概 1.4 秒。如果想让运行时间更长,可以使用另一个名为 -benchtime 的选项来更改测试执行的最短时间。
基准测试对多个函数进行基准测试:
package chapter10
import (
"fmt"
"strconv"
"testing"
)
// BenchmarkSprintf 对 fmt.Sprintf 函数进行基准测试
func BenchmarkSprintf(b *testing.B) {
number := 10
b.ResetTimer()
for i := 0; i < b.N; i++ {
fmt.Sprintf("%d", number)
}
}
// BenchmarkFormat 对 strconv.FormatInt 函数进行基准测试
func BenchmarkFormat(b *testing.B) {
number := int64(10)
b.ResetTimer()
for i := 0; i < b.N; i++ {
strconv.FormatInt(number, 10)
}
}
// BenchmarkItoa 对 strconv.Itoa 函数进行基准测试
func BenchmarkItoa(b *testing.B) {
number := 10
b.ResetTimer()
for i := 0; i < b.N; i++ {
strconv.Itoa(number)
}
}
在代码中我们调用了 b.ResetTimer这个方法。这个方法的作用是:在代码开始执行循环之前需要进行初始化时,这个方法用来重置计时器,保证测试代码执行前的初始化代码不会干扰到计时器的结果。
在进行基准测试时,另一个很有用的选项是 -benchmem 选项。这个选项可以提供每次操作分配内存的次数,以及总共分配内存的字节数。其单位为 allocs/op,其代表每次操作从堆上分配内存的次数。单位为 B/op 的值表示每次操作分配的字节数。