关于 golang-benchmark
面向 Go 开发者的性能测量技能,帮助获得可靠的基准结果,涵盖编写基准、性能分析、统计比较和 CI 回归检测。
主要功能
- 01基准编写与运行指南
- 02分配量和自定义指标测量
- 03CPU、内存与追踪分析
- 04使用 benchstat 进行统计比较
- 05CI 性能回归检测参考
适用场景
- 01优化前测量 Go 代码路径
- 02比较多次基准测试的变更
- 03排查 CPU 或内存开销
- 04在 CI 中检查性能回归
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-benchmark安装后在 Claude Code 中输入/golang-benchmark确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/benchstat.mdci-regression.mdcompiler-analysis.mdinvestigation-session.mdpprof.mdprometheus-go-metrics.mdtools.mdtrace.md详细介绍
角色: 你是一名 Go 性能测量工程师。你绝不会根据单次基准测试得出结论——做出任何优化决策之前,都必须满足统计严谨性和受控条件。
思考模式: 对基准测试分析、性能剖析解读和性能比较任务进行尽可能透彻的推理——深入思考有助于避免误读性能剖析数据,并确保结论在统计上可靠。在 Claude Code 中,使用 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 技能。
编写基准测试
文件与顺序约定
基准测试函数应放在 _bench_test.go 文件中,文件名以被测源文件命名,而不是以单个函数命名——例如,parser.go -> parser_bench_test.go,其中包含 BenchmarkParse、BenchmarkEncode 等,而不是为每个函数单独创建一个 benchmarkparse_test.go。
- 将基准测试放在独立文件中(而不是混在
parser_test.go中),可以让go test -bench=. ./pkg/parser的输出不受无关Test*噪声影响。 - 这样可以将为测量准备的测试夹具(大输入、长生命周期的初始化)与为验证正确性准备的测试夹具分开——二者很少具有相同的形式。
- 该文件仍遵循 Go 的每个源文件对应一个测试文件的约定(→ 请参阅
samber/cc-skills-golang@golang-testing技能),只是用_bench后缀标明其更具体的用途。
在 parser_bench_test.go 中,按 parser.go 里被测函数或方法的顺序排列 Benchmark* 函数——读者从头到尾对照这两个文件时,应能在 Parse 所在的相对位置找到 BenchmarkParse。
b.Loop()(Go 1.24+)——首选
对于 Go 1.24+,编写新的基准测试时优先使用 b.Loop()。它只对循环体计时,并确保函数参数和结果保持存活,从而减少死代码消除造成的错误。
func BenchmarkParse(b *testing.B) {
data := loadFixture("large.json") // setup — excluded from timing
for b.Loop() {
Parse(data) // compiler cannot eliminate this call
}
}
保留现有基准测试或支持 Go <1.24 时,传统的 b.N 循环仍可编译,也可以继续使用。但它更容易出错:可能需要通过 b.ResetTimer() 处理初始化,也可能需要使用结果接收器,以防编译器消除相关工作。Go 1.26 修复了 b.Loop() 早期版本中的内联限制——Go 1.24–1.25 上的基准测试已经能从 b.Loop() 中受益,但可能无法获得 Go 1.26 带来的内联优化。
Go 1.27 的大小专用分配器会改变分配密集型基准测试的基线(小于 80 字节的分配更快,二进制文件更大),这与代码改动无关。如果 benchstat 比较跨越 Go 1.26→1.27 工具链边界,那么测量的就是工具链,而不是代码——在信任性能差异之前,应使用与“优化后”相同的工具链重新运行“优化前”基准测试。
内存跟踪
func BenchmarkAlloc(b *testing.B) {
b.ReportAllocs() // or run with -benchmem flag
var sink []byte
for b.Loop() {
sink = make([]byte, 1024)
}
_ = sink
}
b.ReportMetric() 可添加自定义指标(例如吞吐量):
b.ReportMetric(float64(totalBytes)/b.Elapsed().Seconds(), "bytes/s") // b.Elapsed() is only valid inside 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 表示每次操作的堆分配次数。
并行比较优化方案
当同一瓶颈存在多个相互竞争的优化假设时,通过不同的子 Agent,在各自独立的 worktree 中实现每个方案,避免代码更改在共享工作树中发生冲突。
基准测试应串行运行,不要并行运行。 并行运行的基准测试会共享同一 CPU——邻居噪声效应会污染 ns/op,并重新引入 -count 和 benchstat 本应消除的统计噪声。并行实现是安全的(worktree 相互隔离,不会争用文件);并行测量则不安全(共享硬件,存在实际争用)。一次只运行一个方案的基准测试,可在主工作树中运行,也可在各 worktree 中依次运行。
将每个方案的 benchstat 输出都与同一份基线报告进行比较,保留表现最佳的方案,并移除其余方案的 worktree。
在提交中记录结果
如果更改对性能有可测量的影响,请在提交正文中粘贴 benchstat 输出。这样可以记录优化的_原因_,防止后续读者将其撤销,还能让审阅者无需重新运行基准测试即可验证结论。
提交格式:
perf(parser): reduce Parse allocations 50% with sync.Pool
Replace per-call []byte allocation with a pooled buffer.
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 profile
go test -bench=BenchmarkParse -cpuprofile=cpu.prof ./pkg/parser
go tool pprof cpu.prof
# Memory profile (alloc_objects shows GC churn, inuse_space shows leaks)
go test -bench=BenchmarkParse -memprofile=mem.prof ./pkg/parser
go tool pprof -alloc_objects mem.prof
# Execution trace
go test -bench=BenchmarkParse -trace=trace.out ./pkg/parser
go tool trace trace.out
完整的 pprof CLI 参考(所有命令、非交互模式、性能分析结果解读),请参阅 pprof 参考。执行跟踪结果的解读,请参阅 Trace 参考。统计比较,请参阅 benchstat 参考。
参考文件
-
pprof 参考 — 交互式和非交互式分析 CPU、内存和 goroutine 配置文件。涵盖完整的 CLI 命令、配置文件类型(CPU、alloc*objects 和 inuse_space)、Web UI 导航以及解读模式。需要深入了解代码中时间和内存都花在了哪里时,请使用此参考。
-
benchstat 参考 — 使用严格的置信区间和 p 值检验,对基准测试运行结果进行统计比较。涵盖如何读取输出、筛选旧基准测试、交错结果以便直观比较,以及检测性能回归。需要证明某项更改确实带来了有意义的性能差异,而不只是一次幸运的测试结果时,请使用此参考。
-
Trace 参考 — 用于了解代码何时以及为何运行的执行跟踪器。可视化 goroutine 调度、垃圾回收阶段、网络阻塞和自定义 span 注释。pprof(显示 CPU 用在了哪里)不够用、还需要查看事件时间线时,请使用此参考。
-
诊断工具 — 辅助工具速查:fieldalignment(结构体填充浪费)、GODEBUG(运行时日志标志)、fgprof(帧图配置文件)、竞态检测器(并发错误)等。遇到特定症状并需要有针对性地诊断时,请使用此参考;如果更简单的工具已经能回答问题,就不必使用 pprof。
-
编译器分析 — 编译器优化的底层洞察:逃逸分析(值何时移到堆上)、内联决策(哪些函数调用会被消除)、SSA 转储(中间表示)和汇编输出。基准测试显示存在意料之外的分配,或想验证编译器是否按预期工作时,请使用此参考。
-
CI 回归检测 — 在 CI 流水线中自动检测性能回归并进行门控。涵盖三种工具(用于快速 PR 比较的 benchdiff、基于严格阈值进行门控的 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 运行时指标,以验证基准测试结果