强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

22 - 基准测试:benchmem、pprof、trace、性能分析

22 - 基准测试

22.1 基本基准测试

package bench

import (
    "strings"
    "testing"
)

func BenchmarkConcat(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s := ""
        for j := 0; j < 100; j++ {
            s += "a"
        }
    }
}

func BenchmarkBuilder(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var sb strings.Builder
        for j := 0; j < 100; j++ {
            sb.WriteString("a")
        }
        _ = sb.String()
    }
}
# 运行基准测试
go test -bench=. -benchmem

# 输出示例:
# BenchmarkConcat-8     100000    15000 ns/op    5120 B/op    99 allocs/op
# BenchmarkBuilder-8   1000000     1200 ns/op     512 B/op     8 allocs/op
指标说明
ns/op每次操作耗时(纳秒)
B/op每次操作分配的字节数
allocs/op每次操作的内存分配次数

22.2 基准测试技巧

// 预热(避免编译优化影响)
func BenchmarkFoo(b *testing.B) {
    result := 0
    b.ResetTimer() // 重置计时器(排除初始化时间)
    for i := 0; i < b.N; i++ {
        result = expensiveOperation()
    }
    _ = result // 防止编译器优化掉
}

// 并发基准测试
func BenchmarkFooParallel(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            expensiveOperation()
        }
    })
}

// 子基准测试
func BenchmarkMap(b *testing.B) {
    sizes := []int{100, 1000, 10000}
    for _, size := range sizes {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                m := make(map[int]int, size)
                for j := 0; j < size; j++ {
                    m[j] = j
                }
            }
        })
    }
}

22.3 内存分析

# 内存分配分析
go test -bench=. -benchmem -memprofile=mem.out
go tool pprof mem.out

# 交互模式
(pprof) top
(pprof) top -cum
(pprof) list BenchmarkConcat
(pprof) web  # 生成图形(需要安装 graphviz)

22.4 CPU 分析

# CPU 分析
go test -bench=BenchmarkConcat -cpuprofile=cpu.out
go tool pprof cpu.out

# HTTP 服务器实时分析
import _ "net/http/pprof"

func main() {
    go http.ListenAndServe("localhost:6060", nil)
    // 应用代码...
}

# 访问 http://localhost:6060/debug/pprof/

22.5 trace 工具

# 生成 trace
go test -bench=. -trace=trace.out
go tool trace trace.out

# HTTP 服务器 trace
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5
go tool trace trace.out

22.6 性能优化技巧

// 1. 预分配
func BenchmarkPrealloc(b *testing.B) {
    for i := 0; i < b.N; i++ {
        s := make([]int, 0, 1000)
        for j := 0; j < 1000; j++ {
            s = append(s, j)
        }
    }
}

// 2. 避免不必要的分配
func BenchmarkStringConv(b *testing.B) {
    data := []byte("hello world")
    b.Run("string", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = string(data)
        }
    })
    b.Run("unsafe", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = unsafe.String(unsafe.SliceData(data), len(data))
        }
    })
}

// 3. sync.Pool 减少分配
var pool = sync.Pool{
    New: func() any { return new(bytes.Buffer) },
}

func BenchmarkWithPool(b *testing.B) {
    for i := 0; i < b.N; i++ {
        buf := pool.Get().(*bytes.Buffer)
        buf.Reset()
        buf.WriteString("hello")
        pool.Put(buf)
    }
}

🏢 业务场景

  1. 性能回归检测:CI 中运行基准测试对比性能
  2. 优化决策:比较不同实现方案的性能
  3. 内存泄漏排查:pprof 分析内存增长
  4. CPU 热点定位:pprof 找到最耗时的函数

📖 扩展阅读