golang-benchmark

golang-benchmark

热门

Golang 基准测试、性能分析和性能测量。用于编写、运行或比较 Go 基准测试,使用 pprof 分析热点路径,解释 CPU/内存/跟踪分析结果,使用 benchstat 分析结果,设置 CI 基准回归检测,或使用 Prometheus 运行时指标调查生产性能。当开发者需要对特定性能指标进行深入分析时也可使用——本技能提供测量方法,而 `samber/cc-skills-golang@golang-performance` 提供优化模式。

2261Star
150Fork
更新于 2026/6/6
SKILL.md
只读
名称
golang-benchmark
描述

Golang 基准测试、性能分析和性能测量。用于编写、运行或比较 Go 基准测试,使用 pprof 分析热点路径,解释 CPU/内存/跟踪分析结果,使用 benchstat 分析结果,设置 CI 基准回归检测,或使用 Prometheus 运行时指标调查生产性能。当开发者需要对特定性能指标进行深入分析时也可使用——本技能提供测量方法,而 `samber/cc-skills-golang@golang-performance` 提供优化模式。

角色: 你是一名 Go 性能测量工程师。你绝不会从单次基准测试运行中得出结论——统计严谨性和受控条件是任何优化决策的前提。

思考模式: 在基准测试分析、性能文件解读和性能比较任务中使用 ultrathink。深度推理可防止误解性能分析数据,并确保得出统计上可靠的结论。

依赖:

  • benchstat: go install golang.org/x/perf/cmd/benchstat@latest

Go 基准测试与性能测量

没有测量就没有性能提升——如果你能测量它,就能改进它。

本技能涵盖完整的测量工作流程:编写基准测试、运行它、分析结果、以统计严谨性比较前后结果,并在 CI 中跟踪回归。如需在测量后应用优化模式,→ 参见 samber/cc-skills-golang@golang-performance 技能。如需在运行服务上设置 pprof,→ 参见 samber/cc-skills-golang@golang-troubleshooting 技能。

编写基准测试

b.Loop() (Go 1.24+) — 推荐

对于 Go 1.24+,新基准测试优先使用 b.Loop()。它仅对循环体计时,并保持函数参数/结果存活,从而减少死代码消除错误。

func BenchmarkParse(b *testing.B) {
    data := loadFixture("large.json") // 设置——排除在计时之外
    for b.Loop() {
        Parse(data)  // 编译器无法消除此调用
    }
}

传统的 b.N 循环仍然可以编译,在保留现有基准测试或支持 Go <1.24 时也可以继续使用。但更容易出错:设置可能需要 b.ResetTimer(),如果编译器可以消除工作,结果可能需要一个接收器。Go 1.26 修复了早期 b.Loop() 的内联限制——1.24–1.25 上的基准测试已经受益于 b.Loop(),但可能缺少 1.26 提供的内联优化。

内存跟踪

func BenchmarkAlloc(b *testing.B) {
    b.ReportAllocs() // 或使用 -benchmem 标志运行
    var sink []byte
    for b.Loop() {
        sink = make([]byte, 1024)
    }
    _ = sink
}

b.ReportMetric() 添加自定义指标(例如吞吐量):

b.ReportMetric(float64(totalBytes)/b.Elapsed().Seconds(), "bytes/s") // b.Elapsed() 仅在 b.Loop() 内部有效

子基准测试和表驱动

func BenchmarkEncode(b *testing.B) {
    for _, size := range []int{64, 256, 4096} {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            data := make([]byte, size)
            for b.Loop() {
                Encode(data)
            }
        })
    }
}

运行基准测试

go test -bench=BenchmarkEncode -benchmem -count=10 ./pkg/... | tee bench.txt
标志 用途
-bench=. 运行所有基准测试(正则表达式过滤)
-benchmem 报告分配(B/op, allocs/op)
-count=10 运行 10 次以获得统计显著性
-benchtime=3s 每个基准测试的最短时间(默认 1s)
-cpu=1,2,4 使用不同的 GOMAXPROCS 值运行
-cpuprofile=cpu.prof 写入 CPU 性能文件
-memprofile=mem.prof 写入内存性能文件
-trace=trace.out 写入执行跟踪

输出格式: BenchmarkEncode/size=64-8 5000000 230.5 ns/op 128 B/op 2 allocs/op-8 后缀是 GOMAXPROCS,ns/op 是每次操作的时间,B/op 是每次操作分配的字节数,allocs/op 是每次操作的堆分配次数。

在提交中记录结果

当变更对性能有可测量的影响时,将 benchstat 输出粘贴到提交正文中。这记录了 为什么 进行优化,防止未来的读者回退它,并让审阅者无需重新运行基准测试即可验证声明。

提交格式:

perf(parser): 使用 sync.Pool 将 Parse 分配减少 50%

将每次调用的 []byte 分配替换为池化缓冲区。

goos: linux / goarch: amd64 / cpu: AMD Ryzen 9 5950X
          │    old     │              new               │
          │  sec/op    │  sec/op     vs base            │
Parse-32    4.592µ ± 2%  3.041µ ± 1%  -33.78% (p=0.000 n=10)

          │   old    │             new              │
          │   B/op   │   B/op     vs base           │
Parse-32   1.024Ki ± 0%  0.512Ki ± 0%  -50.00% (p=0.000 n=10)

          │ old  │            new             │
          │ allocs/op │ allocs/op  vs base    │
Parse-32   12.00 ± 0%   6.000 ± 0%  -50.00% (p=0.000 n=10)

规则:

  • 仅包含直接受变更影响的基准测试——删除不相关的行
  • 切勿粘贴包含 ~(无统计显著性)的结果——不能声称有改进
  • 包含硬件上下文行(goos/goarch/cpu),以便结果可重现
  • 对于纯性能变更,使用 perf(scope): 提交类型

从基准测试进行性能分析

直接从基准测试运行生成性能文件——无需 HTTP 服务器:

# CPU 性能文件
go test -bench=BenchmarkParse -cpuprofile=cpu.prof ./pkg/parser
go tool pprof cpu.prof

# 内存性能文件(alloc_objects 显示 GC 抖动,inuse_space 显示泄漏)
go test -bench=BenchmarkParse -memprofile=mem.prof ./pkg/parser
go tool pprof -alloc_objects mem.prof

# 执行跟踪
go test -bench=BenchmarkParse -trace=trace.out ./pkg/parser
go tool trace trace.out

有关完整的 pprof CLI 参考(所有命令、非交互模式、性能文件解读),请参见 pprof 参考。有关执行跟踪解读,请参见 跟踪参考。有关统计比较,请参见 benchstat 参考

参考文件

  • pprof 参考 — CPU、内存和 goroutine 性能文件的交互式和非交互式分析。完整的 CLI 命令、性能文件类型(CPU vs alloc*objects vs inuse_space)、Web UI 导航和解读模式。用于深入分析代码中时间和内存的消耗位置。

  • benchstat 参考 — 使用严格的置信区间和 p 值测试对基准测试运行进行统计比较。涵盖输出读取、过滤旧基准测试、交错结果以获得视觉清晰度以及回归检测。用于需要证明变更产生了有意义的性能差异,而不仅仅是幸运的运行。

  • 跟踪参考 — 执行跟踪器,用于理解代码运行的 时间原因。可视化 goroutine 调度、垃圾回收阶段、网络阻塞和自定义跨度注释。当 pprof(显示 CPU 消耗位置)不够用时使用——你需要查看事件的时间线。

  • 诊断工具 — 辅助工具的快速参考:fieldalignment(结构体填充浪费)、GODEBUG(运行时日志标志)、fgprof(帧图性能文件)、竞态检测器(并发错误)等。当你有特定症状并需要针对性诊断时使用——如果更简单的工具已经能回答你的问题,就不要使用 pprof。

  • 编译器分析 — 底层编译器优化见解:逃逸分析(值何时移动到堆)、内联决策(哪些函数调用被消除)、SSA 转储(中间表示)和汇编输出。当基准测试显示你未预期的分配,或你想验证编译器是否按预期执行时使用。

  • CI 回归检测 — CI 流水线中的自动性能回归门控。涵盖三种工具(benchdiff 用于快速 PR 比较,cob 用于严格的阈值门控,gobenchdata 用于长期趋势仪表板)、嘈杂邻居缓解策略(为什么云 CI 基准测试即使在安静机器上也有 5-10% 的波动)以及自托管运行器调优以使基准测试可重现。用于确保拉取请求不会悄无声息地降低代码库速度——尽早检测回归可防止性能债务的产生。

  • 调查会话 — 结合 Prometheus 运行时指标(堆大小、GC 频率、goroutine 数量)、PromQL 查询以关联指标与代码变更、运行时配置标志(启用 GC 日志记录的 GODEBUG 环境变量)以及成本警告(当你遇到性能税时)的生产性能故障排除工作流程。当生产基准测试看起来不错但实际流量表现不同时使用。

  • Prometheus Go 指标参考 — 由 prometheus/client_golang 实际暴露为 Prometheus 指标的 Go 运行时指标的完整列表。涵盖 30 个默认指标、40+ 个可选指标(Go 1.17+)、进程指标和常见 PromQL 查询。区分 runtime/metrics(Go 内部数据)和 Prometheus 指标(从 /metrics 抓取的内容)。用于设置监控仪表板或为生产告警编写 PromQL 查询。

交叉引用

  • → 参见 samber/cc-skills-golang@golang-performance 技能,了解测量后应用的优化模式("如果 X 是瓶颈,应用 Y")
  • → 参见 samber/cc-skills-golang@golang-troubleshooting 技能,了解运行服务上的 pprof 设置(启用、安全、捕获)、Delve 调试器、GODEBUG 标志、根本原因方法论
  • → 参见 samber/cc-skills-golang@golang-observability 技能,了解日常始终在线的监控、持续性能分析(Pyroscope)、分布式追踪(OpenTelemetry)
  • → 参见 samber/cc-skills-golang@golang-testing 技能,了解通用测试实践
  • → 参见 samber/cc-skills@promql-cli 技能,用于在生产环境中查询 Prometheus 运行时指标以验证基准测试结果