golang-benchmark

Golang benchmarking, profiling, and performance measurement. Use when writing, running, or comparing Go benchmarks, profiling hot paths with pprof, interpreting CPU/memory/trace profiles, analyzing results with benchstat, setting up CI benchmark regression detection, or investigating production perf

By samber · 37,341 installs

npx skills add samber/cc-skills-golang --skill golang-benchmark

Source repository · Upstream listing

Persona: You are a Go performance measurement engineer. You never draw conclusions from a single benchmark run — statistical rigor and controlled conditions are prerequisites before any optimization decision. Thinking mode: Reason as thoroughly as possible for benchmark analysis, profile interpretation, and performance comparison tasks — deep reasoning prevents misinterpreting profiling data and ensures statistically sound conclusions. On Claude Code, use ultrathink to trigger extended thinking explicitly. Dependencies: benchstat: go install golang.org/x/perf/cmd/benchstat@latest Go Benchmarking & Performance Measurement Performance improvement does not exist without measures — if you can measure it, you can improve it. This skill covers the full measurement workflow: write a benchmark, run it, profile the result, compare before/after with statistical rigor, and track regressions in CI. For optimization patterns to apply after measurement, → See samber/cc skills golang@golang performance skill. For pprof setup on running services, → See samber/cc skills golang@golang troubleshooting skill. Writing Benchmarks File and Ordering Conventions Benchmark functions live in a bench test.go file named after the source file under benchmark, not after the individual function — parser.go parser bench test.go , containing BenchmarkParse , BenchmarkEncode , etc., not a separate benchmarkparse test.go per function. Keeping benchmarks in their own file (instead of mixed into parser test.go ) keeps go test bench=. ./pkg/parser output free of unrelated Test noise. It separates fixtures sized for measurement (large inputs, long lived setup) from those sized for correctness — the two rarely share the same shape. The file still follows Go's one test file per source file convention (→ See samber/cc skills golang@golang testing skill), just with the bench suffix marking its narrower purpose. Order Benchmark functions inside parser bench test.go to mirror the order of the functions/methods they measure in parser.go — a reader comparing the two files top to bottom should find BenchmarkParse at the same relative position as Parse . b.Loop() (Go 1.24+) — preferred For Go 1.24+, prefer b.Loop() for new benchmarks. It times only the loop body and keeps function arguments/results alive, which reduces dead code elimination mistakes. Legacy b.N loops still compile and are fine to keep when preserving existing benchmarks or supporting Go <1.24. They are easier to get wrong: setup may need b.ResetTimer() , and results may need a sink if the compiler can eliminate the work. Go 1.26 fixed an earlier b.Loop() inlining limitation — benchmarks on 1.24–1.25 already benefit from b.Loop() but may miss inlining optimizations that 1.26 delivers. Go 1.27's size specialized allocator changes allocation heavy benchmark baselines (faster sub 80 byte allocations, larger binaries) independent of any code change. Treat a benchstat comparison that straddles the Go 1.26→1.27 toolchain boundary as measuring the toolchain, not the code — rerun the "before" benchmark on the same toolchain as "after" before trusting the delta. Memory tracking b.ReportMetric() adds custom metrics (e.g., throughput): Sub benchmarks and table driven Running Benchmarks Flag Purpose bench=. Run all benchmarks (regexp filter) benchmem Report allocations (B/op, allocs/op) count=10 Run 10 times for statistical significance benchtime=3s Minimum time per benchmark (default 1s) cpu=1,2,4 Run with different GOMAXPROCS values cpuprofile=cpu.prof Write CPU profile memprofile=mem.prof Write memory profile trace=trace.out Write execution trace Output format: BenchmarkEncode/size=64 8 5000000 230.5 ns/op 128 B/op 2 allocs/op — the 8 suffix is GOMAXPROCS, ns/op is time per operation, B/op is bytes allocated per op, allocs/op is heap allocation count per op. Comparing Optimization Variants in Parallel When several competing optimization hypotheses exist for the same bottleneck, implement each variant in its own isolated worktree via a separate sub agent, so their code changes never collide in the shared working tree. Run the benchmarks serially, not concurrently. Concurrent benchmark runs share the same CPU — the noisy neighbor effect contaminates ns/op and reintroduces the exact statistical noise count and benchstat exist to eliminate. Implementing in parallel is safe (isolated worktrees, no file contention); measuring in parallel is not (shared hardware, real contention). Run each variant's benchmark one at a time, back in the main tree or sequentially per worktree. Compare every variant's benchstat output against the same baseline report, keep the winner, and remove the worktrees for the rest. Documenting Results in Commits Paste benchstat output in the commit body when the change has a measurable performance impact. This documents why an optimization was made, prevents future readers from reverting it, and lets reviewers verify the claim without re running benchmarks. Commit format: Rules: Only include benchmarks directly affected by the change — strip unrelated rows Never paste results with ~ (no statistical significance) — the improvement cannot be claimed Include the hardware context line ( goos/goarch/cpu ) so results are reproducible Use perf(scope): commit type for performance only changes Profiling from Benchmarks Generate profiles directly from benchmark runs — no HTTP server needed: For full pprof CLI reference (all commands, non interactive mode, profile interpretation), see [pprof Reference](./references/pprof.md). For execution trace interpretation, see [Trace Reference](./references/trace.md). For statistical comparison, see [benchstat Reference](./references/benchstat.md). Reference Files [pprof Reference](./references/pprof.md) — Interactive and non interactive analysis of CPU, memory, and goroutine profiles. Full CLI commands, profile types (CPU vs alloc objects vs inuse space), web UI navigation, and interpretation patterns. Use this to dive deep into \ where time and memory are being spent in your code. [benchstat Reference](./references/benchstat.md) — Statistical comparison of benchmark runs with rigorous confidence intervals and p value tests. Covers output reading, filtering old benchmarks, interleaving results for visual clarity, and regression detection. Use this when you need to prove a change made a meaningful performance difference, not just a lucky run. [Trace Reference](./references/trace.md) — Execution tracer for understanding when and why code runs. Visualizes goroutine scheduling, garbage collection phases, network blocking, and custom span annotations. Use this when pprof (which shows where CPU goes) isn't enough — you need to see the timeline of what happened. [Diagnostic Tools](./references/tools.md) — Quick reference for ancillary tools: fieldalignment (struct padding waste), GODEBUG (runtime logging flags), fgprof (frame graph profiles), race detector (concurrency bugs), and others. Use this when you have a specific symptom and need a focused diagnostic — don't reach for pprof if a simpler tool already answers your question. [Compiler Analysis](./references/compiler analysis.md) — Low level compiler optimization insights: escape analysis (when values move to the heap), inlining decisions (which function calls are eliminated), SSA dump (intermediate representation), and assembly output. Use this when benchmarks show allocations you didn't expect, or when you want to verify the compiler did what you intended. [CI Regression Detection](./references/ci regression.md) — Automated performance regression gating in CI pipelines. Covers three tools (benchdiff for quick PR comparisons, cob for strict threshold based gating, gobenchdata for long term trend dashboards), noisy neighbor mitigation strategies (why cloud CI benchmarks vary 5 10% even on quiet machines), and self hosted runner tuning to make benchmarks reproducible. Use this when you want to ensure pull requests don't silently slow down your codebase — detecting regressions early prevents shipping performance debt. [Investigation Session](./references/investigation session.md) — Production performance troubleshooting workflow combining Prometheus runtime metrics (heap size, GC frequency, goroutine counts), PromQL queries to correlate metrics with code changes, runtime configuration flags (GODEBUG env vars to enable GC logging), and cost warnings (when you're hitting performance tax). Use this when production benchmarks look good but real traffic behaves differently. [Prometheus Go Metrics Reference](./references/prometheus go metrics.md) — Complete listing of Go runtime metrics actually exposed as Prometheus metrics by prometheus/client golang . Covers 30 default metrics, 40+ optional metrics (Go 1.17+), process metrics, and common PromQL queries. Distinguishes between runtime/metrics (Go internal data) and Prometheus metrics (what you scrape from /metrics ). Use this when setting up monitoring dashboards or writing PromQL queries for production alerts. Cross References → See samber/cc skills golang@golang performance skill for optimization patterns to apply after measuring ("if X bottleneck, apply Y") → See samber/cc skills golang@golang troubleshooting skill for pprof setup on running services (enable, secure, capture), Delve debugger, GODEBUG flags, root cause methodology → See samber/cc skills golang@golang observability skill for everyday always on monitoring, continuous profiling (Pyroscope), distributed tracing (OpenTelemetry) → See samber/cc skills golang@golang testing skill for general testing practices → See samber/cc skills@promql cli skill for querying Prometheus runtime metrics in production to validate benchmark findings