Go通过Benchmark对代码进行性能测试详解
前言
性能是软件开发中的一个重要指标,因为良好的性能可以提高软件的运行效率,增强用户体验。在Go语言中,有一种叫做benchmark的工具可以用来测试代码在特定条件下的性能表现。在本文中,我们将介绍如何使用Go的benchmark工具进行性能测试。
创建Benchmark函数
在Go语言中,一个benchmark测试函数必须满足以下要求:
- 函数名称以Benchmark开头;
- 函数有一个参数类型为*testing.B;
- 函数头部必须包含t.ResetTimer(),用于重置计时器;
- 函数头部必须包含t.StartTimer(),用于启动计时器;
- 函数尾部必须包含t.StopTimer(),用于停止计时器。
以下是实现一个简单的benchmark测试的示例:
package main
import (
"math/rand"
"testing"
)
func BenchmarkRandomInt(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Intn(100)
}
}
在上面的示例中,我们编写了一个名为BenchmarkRandomInt的测试函数,每次循环通过rand.Intn(100)生成一个随机整数。接下来,我们将演示该函数如何执行benchmark测试。
运行Benchmark测试
运行benchmark测试非常简单,只需要在终端中使用命令go test -bench=.
即可。其中,-bench参数用于指定要运行的Benchmark测试函数的名称,点号表示运行所有的Benchmark测试函数。运行结果如下所示:
goos: darwin
goarch: amd64
pkg: github.com/mysite/myapp
BenchmarkRandomInt-4 2000000 713 ns/op
PASS
ok github.com/mysite/myapp 2.449s
从上面的输出结果可以看出,BenchmarkRandomInt函数执行了2000000次,每次执行的平均时间为713纳秒(ns)。除了平均时间之外,还有很多其他信息可以帮助我们评估代码的性能表现,比如每个测试用例的执行时间、内存分配量等等。
示例说明
示例一
以下是一个基于切片实现的简单排序算法的代码:
package main
import (
"fmt"
)
func bubbleSort(array []int) {
for i := 0; i < len(array)-1; i++ {
for j := 0; j < len(array)-i-1; j++ {
if array[j] > array[j+1] {
array[j], array[j+1] = array[j+1], array[j]
}
}
}
}
func main() {
array := []int{5, 3, 6, 2, 9}
bubbleSort(array)
fmt.Println(array)
}
为了测试这个排序算法的性能,我们编写一个benchmark测试函数BenchmarkBubbleSort,测试该算法对于1000个随机整数的处理时间:
func BenchmarkBubbleSort(b *testing.B) {
data := make([]int, 1000)
for i := 0; i < 1000; i++ {
data[i] = rand.Intn(1000)
}
for i := 0; i < b.N; i++ {
bubbleSort(data)
}
}
运行测试命令go test -bench=BubbleSort -run=^$ -benchtime=3s -cpu=1,2,4,8
,其中-benchtime用于指定执行时间,-cpu用于指定CPU核心数量。执行结果如下所示:
BenchmarkBubbleSort-4 179547 20653 ns/op
BenchmarkBubbleSort-4 740131 4099 ns/op
BenchmarkBubbleSort-4 2398084 1422 ns/op
BenchmarkBubbleSort-4 3041276 995 ns/op
从上述结果可以看出,在不同CPU核心数量下,测试函数的执行时间和操作次数是相应变化的。
示例二
考虑以下的常规任务:
- 从一个列表中随机选择一个元素。
- 如果选择的元素小于10,则将它除以10,否则将它乘以2。
- 对结果取整后返回。
接下来,我们将定义一个带有三个步骤的函数,然后将进行好几次关于这个函数的基准测试。使用下面的代码创建函数和基准测试:
func TestChooser(t *testing.T) {
tests := []struct {
desc string
items []int
expected int
}{
{"returns a number below 10 unmodified", []int{2, 4, 6, 8}, 2},
{"scales a number above 10", []int{16, 20, 40, 80}, 32},
{"test10x", bigList, 42},
{"test100x", hugeList, 42},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
rando := func() int { return test.items[rand.Intn(len(test.items))] }
if v := choose(rando, rand.Intn); v != test.expected {
t.Errorf("got %d, want %d", v, test.expected)
}
})
}
}
func BenchmarkChooser(b *testing.B) {
for i := 0; i < b.N; i++ {
choose(rand.Intn, rand.Intn)
}
}
func BenchmarkChooserAbove10(b *testing.B) {
for i := 0; i < b.N; i++ {
choose(scaledIntGen(10, 20), scaledIntGen(20, 40))
}
}
func BenchmarkChooserAbove100(b *testing.B) {
for i := 0; i < b.N; i++ {
choose(scaledIntGen(100, 200), scaledIntGen(200, 400))
}
}
var bigList []int
var hugeList []int
func init() {
var maxI int
if testing.Short() {
maxI = 10000
} else {
maxI = 100000
}
bigList = make([]int, maxI)
hugeList = make([]int, maxI*10)
for i := 0; i < maxI*10; i++ {
hugeList[i] = rand.Int()
}
for i := 0; i < maxI; i++ {
bigList[i] = i
}
}
func scaledIntGen(low, high int) func() int {
return func() int {
if rand.Intn(2) == 1 {
return rand.Intn(low)
}
return rand.Intn(high) + low
}
}
func choose(getter1 func() int, getter2 func() int) int {
i := getter1()
if i < 10 {
i *= 10
} else {
i *= 2
}
j := getter2()
if j < 10 {
j *= 10
} else {
j *= 2
}
return (i + j) / 2
}
运行测试的命令是:
go test -bench=. -benchtime=1000x
这个输出的结果是:
BenchmarkChooser-4 1000 8167780 ns/op
BenchmarkChooserAbove10-4 1000 8243747 ns/op
BenchmarkChooserAbove100-4 1000 8212555 ns/op
上述的输出告诉我们,一千个运行时间中,分别花费了 8167780ns(0.00816毫秒)、8243747ns(0.00824毫秒)和 8212555(0.00821毫秒)。
结论
在本文中,我们详细讲解了如何使用Go语言的benchmark工具对代码进行性能测试,包括Benchmark测试函数的创建、运行Benchmark测试以及两个示例说明。性能测试是编写高质量代码的基本要求之一,希望这篇文章能够帮助你更好地学习和使用Go语言。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:go通过benchmark对代码进行性能测试详解 - Python技术站