引入

测试验证功能正确,但性能呢?Go 的 testing 包自带基准测试,用 Benchmark 函数测量代码执行耗时,找出性能瓶颈,不用装额外工具。

正文

定义

定义

基准测试(benchmark)用来测量代码执行性能。测试函数以 Benchmark 开头,签名为 func BenchmarkXxx(b *testing.B),框架自动调整 b.N 的迭代次数直到结果稳定。

语法

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}

例子

基本基准测试

func BenchmarkFmtSprint(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Sprint("hello")
    }
}

运行 go test -bench=. 后,框架自动确定 b.N 的大小,输出每次操作的平均耗时。

带初始化的基准测试

func BenchmarkSort(b *testing.B) {
    data := make([]int, 1000)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        sort.Ints(data)
    }
}

b.ResetTimer() 清掉初始化耗时,只测目标代码的性能。

子基准测试

func BenchmarkSort(b *testing.B) {
    sizes := []int{10, 100, 1000}
    for _, size := range sizes {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            data := make([]int, size)
            for i := range data {
                data[i] = size - i
            }
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                sort.Ints(data)
            }
        })
    }
}

子基准测试用 b.Run() 分组,能直观对比不同输入规模下的性能差异。

内存分配统计

func BenchmarkAlloc(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s := make([]byte, 1024)
        _ = s
    }
}

-benchmem 参数输出每次操作的内存分配次数和字节数

常见写法

写法说明
go test -bench=.运行所有基准测试
go test -bench=Name运行指定基准测试
go test -benchmem显示内存分配
go test -bench=. -count=3重复运行 3 次
b.ResetTimer()重置计时器(忽略初始化耗时)
b.StopTimer()暂停计时
b.StartTimer()恢复计时
b.ReportAllocs()输出内存分配统计
b.Run(name, fn)创建子基准测试

特点

  • 基准测试函数必须以 Benchmark 开头,参数是 *testing.B
  • b.N 由框架自动调整,不需要手动设定
  • 循环体必须用 for i := 0; i < b.N; i++ 包裹目标代码
  • b.ResetTimer() 用来排除初始化开销
  • 结果单位是 ns/op(纳秒每次操作),越小越好
  • -benchmem 能看到 allocs/opB/op
  • 同一测试跑多次(-count)能减少偶然误差

理解

基准测试就是给代码跑个分。b.N 自动调大小,测出来的 ns/op 稳定可靠。配合 b.ResetTimer() 排除干扰,-benchmem 看内存分配,基本能定位大部分性能问题。不用写专门的压测工具,go test 一条命令全搞定。

引出

基准测试发现了性能瓶颈,接下来怎么构建、检查和格式化代码?看 Go 工具链,了解 Go 自带的开发工具全家桶。