likes
comments
collection
share

Go 编程 | 连载 34 - Benchmark 基准测试

作者站长头像
站长
· 阅读数 17

一、基准测试 Benchmark

Go 编程 | 连载 33 - UnitTest 单元测试 中实现了 Go 的单元测试用例,单元测试的一般形式为:

TestXxx(t *testing.T)

除此之外 Go 的 testing 标准库还包含一个强大的基准测试,基准测试可以反复的运行函数,从而建立基准,并且无须执行运行次数,因为框架会通过调整次数来获得可靠的数据集,基准测试结束后将获得一个报告,该保函中包含了运行次数以及运行一次消耗的时间,单位为 ns。

基准测试函数的命名方式为 BenchmarkXxx 并且要求传入一个 *testing.B 类型。

BenchmarkXxx(b *testing.B) 

新建一个 fib.go 文件,增加一个函数 Fib

func Fib(n int) int {
   if n < 2 {
      return n
   }
   return Fib(n-1) + Fib(n-2)
}

新增一个测试文件 benchmark_fib_test.go,增加测试函数 BenchmarkFib

func BenchmarkFib(b *testing.B) {
   for n := 0; n < b.N; n++ {
      Fib(20)
   }
}

执行该测试文件,输出内容如下:

goos: darwin
goarch: amd64
pkg: go-advanced/10-test
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
BenchmarkFib
BenchmarkFib-8   	   26546	     45176 ns/op
PASS

使用 IDEA 运行基准测试时,要注意基准测试用例的名字一定要是 BenchmarkXxx 形式,否则会报错,将函数名改为 FibTest,再次执行测试。

Go 编程 | 连载 34 - Benchmark 基准测试

基准测试函数的名字为 BenchmarkXxx 时会自动使用 go bench 执行测试。

Go 编程 | 连载 34 - Benchmark 基准测试

基准测试可以通过程序来确定完成特定任务时性能最佳的方式是哪一种。

新建一个 tango.go 文件,定义三个函数实现拼接字符串。

func StringFromAssignment(x int) string {
   var s string
   
   for i := 0; i < x; i++ {
      s += "tango"
   }
   return s
}

func StringFromAppendJoin(x int) string {
   s := []string{}

   for i := 0; i < x; i++ {
      s = append(s, "tango")
   }

   return strings.Join(s, "")
}

func StringFromBuffer(x int) string {
   var buffer bytes.Buffer

   for i := 0; i < x; i++ {
      buffer.WriteString("tango")
   }

   return buffer.String()
}

同级目录下新建一个 tango_benchmark_test.go 文件,定义三个函数的基准测试函数。

func BenchmarkStringFromAssignment(b *testing.B) {
   for i := 0; i < b.N; i++ {
      StringFromAssignment(100)
   }
}

func BenchmarkStringFromAppendJoin(b *testing.B) {
   for i := 0; i < b.N; i++ {
      StringFromAppendJoin(100)
   }
}

func BenchmarkStringFromBuffer(b *testing.B) {
   for i := 0; i < b.N; i++ {
      StringFromBuffer(100)
   }
}

执行基准测试,输出结果如下

goos: darwin
goarch: amd64
pkg: go-advanced/10-test
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
BenchmarkStringFromAssignment
BenchmarkStringFromAssignment-8   	  148418	      7382 ns/op
BenchmarkStringFromAppendJoin
BenchmarkStringFromAppendJoin-8   	  555007	      2001 ns/op
BenchmarkStringFromBuffer
BenchmarkStringFromBuffer-8       	 1422670	       818.5 ns/op
PASS

可以看出使用 StringFromBuffer 函数拼接字符串耗时最短,效率最高。

二、testing.B 类型

*testing.B 类型是基准测试函数的入参,类似单元测试中的 *testing.T,也适用于管理测试的行为。

*testing.B*testing.T 类型一样都组合了 common 结构体,所有也都拥有 FailNowSkipNowSkipf 等方法来结束测试并输出格式化错误信息。

Go 编程 | 连载 34 - Benchmark 基准测试

*testing.B 类型可以用于管理基准测试的计时行为,有三个方法用于计时:

  • StartTimer:开始对测试进行计时。该方法会在基准测试开始时自动被调用,也可以在调用 StopTimer 之后恢复计时;
  • StopTimer:停止对测试进行计时。当需要执行一些复杂的初始化操作,并且不想对这些操作进行测量时,就可以使用这个方法来暂时地停止计时;
  • ResetTimer:对已经逝去的基准测试时间以及内存分配计数器进行清零。对于正在运行中的计时器,这个方法不会产生任何效果。

Go 编程 | 连载 34 - Benchmark 基准测试

基准测试还可以统计内存消耗以及指定运行时间等,只需要在命令行运行时添加相应的参数即可。