Generated banner for Golang Benchmark
Generated logo for Golang Benchmark

Golang Benchmark

Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk

From the author

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 performance with Prometheus runtime metrics. Also use when the developer needs deep analysis on a specific performance indicator - this skill provides the measurement methodology, while `samber/cc-skills-golang@golang-performance` provides the optimization patterns.
— the description by samber

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a rigorous methodology for measuring Go performance. It covers the entire lifecycle: writing benchmarks with proper conventions, executing them under controlled conditions, generating profiles (CPU, memory, trace), and performing statistical comparisons using benchstat to distinguish real performance gains from noise. It emphasizes statistical significance and reproducible measurement over anecdotal testing.

What it does

  • Guides the creation of Go benchmarks using standard naming and file conventions.
  • Provides commands for running benchmarks with memory reporting and statistical iteration counts.
  • Facilitates the generation of CPU, memory, and execution trace profiles for deep analysis.
  • Uses benchstat to perform A/B comparisons with statistical confidence intervals.
  • Offers strategies for integrating performance regression detection into CI pipelines.
  • Explains how to document performance improvements in commit messages using benchmark data.

When to use it

  • When you need to measure the performance impact of a code change with statistical rigor.
  • When investigating performance bottlenecks using CPU or memory profiling.
  • When setting up automated performance regression gating in CI.
  • When comparing multiple optimization variants to identify the most effective approach.
  • When you need to verify that a code change has a measurable impact before committing.

Advantages

  • Promotes statistical rigor, preventing misleading conclusions from single benchmark runs.
  • Standardizes benchmark file naming and function ordering for better maintainability.
  • Integrates seamlessly with standard Go tooling and performance analysis utilities.
  • Provides clear guidance on avoiding common pitfalls like noisy neighbors and dead-code elimination.
  • Enables data-driven performance documentation in git history.

Usage examples

“How do I run my benchmarks 10 times to ensure statistical significance?”
The agent will provide the command: go test -run='^$' -bench=. -benchmem -count=10 ./pkg/...
“Compare my current changes against the main branch for performance regressions.”
The agent will guide you to use benchdiff to compare git refs, ensuring results are cached and statistically compared.
“How do I generate and analyze a CPU profile for my benchmark?”
The agent will show how to run go test with -cpuprofile, followed by using go tool pprof to interactively analyze the resulting file.
“How should I format my commit message when I optimize a function?”
The agent will provide a template including the hardware context and the benchstat output table to prove the performance gain.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/samber/golang-benchmark/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip golang-benchmark.zip -d .agents/skills

Documentation by samber

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.

func BenchmarkParse(b *testing.B) {
    data := loadFixture("large.json") // setup — excluded from timing
    for b.Loop() {
        Parse(data)  // compiler cannot eliminate this call
    }
}

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
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() adds custom metrics (e.g., throughput):

b.ReportMetric(float64(totalBytes)/b.Elapsed().Seconds(), "bytes/s") // b.Elapsed() is only valid inside b.Loop()
Sub-benchmarks and table-driven
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)
            }
        })
    }
}

Running Benchmarks

go test -bench=BenchmarkEncode -benchmem -count=10 ./pkg/... | tee bench.txt
FlagPurpose
-bench=.Run all benchmarks (regexp filter)
-benchmemReport allocations (B/op, allocs/op)
-count=10Run 10 times for statistical significance
-benchtime=3sMinimum time per benchmark (default 1s)
-cpu=1,2,4Run with different GOMAXPROCS values
-cpuprofile=cpu.profWrite CPU profile
-memprofile=mem.profWrite memory profile
-trace=trace.outWrite 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:

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)

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:

# 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

For full pprof CLI reference (all commands, non-interactive mode, profile interpretation), see pprof Reference. For execution trace interpretation, see Trace Reference. For statistical comparison, see benchstat Reference.

Reference Files

  • pprof Reference — Interactive and non-interactive analysis of CPU, memory, and goroutine profiles. Full CLI commands, profile types (CPU vs allocobjects 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 — 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 — 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 — 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 — 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 — 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 — 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 — 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

Files

references/benchstat.md

benchstat Reference

benchstat computes statistical summaries and A/B comparisons of Go benchmark results. A single benchmark run tells you nothing about variance — benchstat tells you whether the difference between two runs is real or noise.

Table of Contents

  • Installation

  • Usage

  • Basic Workflow

    • Step 0: Write benchmarks

    • Step 1: Measure baseline

    • Step 2: Make your change

    • Step 3: Measure again

    • Step 4: Compare

  • Reading the Output

    • Unit normalization

    • When the ~ symbol appears

  • Flags Reference

    • Projection flags

    • Filter flag

    • Input labeling

  • Filter Expression Syntax

    • Matching operators

    • Logical operators

    • Filter key types

    • Filter examples

  • Projection Examples

    • Default: before/after file comparison

    • Compare sub-benchmark parameters within a single file

    • Simplify rows to base name only

    • Control column order

    • Group by GOMAXPROCS

    • Separate tables per package

    • Ignore a dimension

    • Compare three versions

    • Cross-dimensional comparison

  • Unit Metadata

    • assume=exact

    • assume=nothing (default)

  • Interleaving Runs

  • How Many Runs?

  • Single-File Summary

  • Common Pitfalls

  • benchstat in CI

Installation

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

Usage

benchstat [flags] inputs...

Each input is a file containing go test -bench output. Optionally label inputs with label=path syntax.

Basic Workflow

Step 0: Write benchmarks

Use the standard Go benchmark function signature in *_test.go:

Step 1: Measure baseline

Run benchmarks with -count=10 or more. Each run produces one data point — you need at least 10 to compute a meaningful confidence interval:

go test -run='^$' -bench=BenchmarkParse -benchmem -count=10 ./pkg/parser | tee old.txt

-run='^$' skips unit tests so only benchmarks run — avoids wasting time on tests during measurement sessions.

Step 2: Make your change

Edit the code you want to optimize.

Step 3: Measure again

Same command, same flags, same machine, same load conditions:

go test -run='^$' -bench=BenchmarkParse -benchmem -count=10 ./pkg/parser | tee new.txt
Step 4: Compare
benchstat old.txt new.txt

Output:

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

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

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

Reading the Output

ElementMeaningWhat to look for
median (e.g., 4.592µ)Central value across runs — more robust than mean because outliers don't skew itThe reference number for this benchmark
± N% (e.g., ± 2%)Half-width of the 95% confidence interval as a percentage of the medianLow (≤2%) = stable measurement. High (>5%) = noisy — investigate noise sources before trusting results
vs base (e.g., -33.78%)Percentage change from the first input (base) to subsequent inputsNegative = faster/smaller. Positive = slower/larger
p=N (e.g., p=0.000)p-value from Mann-Whitney U-test (non-parametric)<0.05 = statistically significant. ≥0.05 = difference could be noise
n=N (e.g., n=10)Number of samples used in the comparisonShould usually match your -count; if it does not, check that each input file contains the same benchmark rows and units
~No statistically significant difference detectedDo NOT claim improvement — the change might be zero
geomean rowGeometric mean of changes across all benchmarks in the tableOverall proportional change; useful when comparing many benchmarks at once
Unit normalization

benchstat automatically normalizes units for display:

  • ns/op → displayed as sec/op (with µ, m prefixes) to avoid nonsensical µns/op

  • MB/s → displayed as B/s (with K, M, G prefixes)

When the ~ symbol appears
Parse-32    4.592µ ± 8%   4.481µ ± 7%  ~ (p=0.089 n=10)

This means benchstat cannot distinguish the difference from random noise. The wide confidence intervals (±8%, ±7%) overlap. Do not claim improvement. Options:

  • Increase -count to 20+ (narrower CI may reveal a real difference)

  • Reduce noise sources (close applications, plug in power, use dedicated machine)

  • Accept that the change has no measurable effect on this benchmark

Flags Reference

Projection flags

These flags control how benchmark results are grouped into tables, rows, and columns.

FlagDefaultPurpose
-table KEYS.configGroup results into separate tables by these keys
-row KEYS.fullnameGroup results into table rows by these keys
-col KEYS.fileCompare across columns with different values of these keys
-ignore KEYS(none)Omit keys from grouping — suppresses "benchmarks vary" warnings

Available keys:

KeyMeaningExample value
.nameBase benchmark name (without sub-benchmark config)Parse from BenchmarkParse/size=4k-16
.fullnameFull name including sub-benchmark configurationParse/size=4k-16
.fileInput file name or custom labelold.txt or baseline
.configAll file-level configuration keys combinedgoos/goarch/pkg/cpu
.unitMetric unit namesec/op, B/op, allocs/op
/{name-key}Per-benchmark sub-name key/size extracts 4k from Parse/size=4k
/gomaxprocsGOMAXPROCS value — recognizes both /gomaxprocs=N and the -N suffix convention16 from Parse-16
goosOperating system (from benchmark output header)linux, darwin
goarchArchitecture (from benchmark output header)amd64, arm64
pkgPackage path (from benchmark output header)myapp/pkg/parser
cpuCPU model (from benchmark output header)AMD Ryzen 9 5950X

Sort order modifiers — append to any key:

ModifierMeaningExample
@alphaAlphabetic sort/format@alpha
@numNumeric sort (understands prefixes: 2k, 1Mi)/size@num
@(val1 val2 ...)Fixed order + filter (only listed values, in this order)/format@(gob json)
Filter flag
FlagPurpose
-filter EXPRFilter which benchmarks are processed before grouping and comparison

See Filter Expression Syntax below for full details.

Input labeling

Not a flag but a syntax feature — label input files for clearer column headers:

# Default: file names become column headers
benchstat old.txt new.txt

# Custom labels
benchstat baseline=old.txt optimized=new.txt

# Multiple versions
benchstat v1=v1.txt v2=v2.txt v3=v3.txt

The first input is always the base for comparison. All subsequent inputs are compared against it.

Filter Expression Syntax

Filters select which benchmarks to include before grouping and comparison. The syntax is:

Matching operators
PatternMeaningExample
key:valueExact matchgoos:linux
key:"value"Exact match with quoted value (allows spaces, special chars)pkg:"github.com/user/repo"
key:/regexp/Regular expression match (Go regexp syntax).name:/Parse|Encode/
key:(val1 OR val2)Match any of the listed valuesgoos:(linux OR darwin)
*Match everything (all benchmarks)*
Logical operators
OperatorMeaningExample
x yAND — both must match (implicit)goos:linux goarch:amd64
x AND yAND — explicit formgoos:linux AND goarch:amd64
x OR yOR — either must matchgoos:linux OR goos:darwin
-xNOT — must not match-goos:windows
(...)Grouping / subexpression(goos:linux OR goos:darwin) -pkg:/internal/
Filter key types
KeyWhat it matchesExample
.nameBase benchmark name.name:Parse
.fullnameFull name with sub-benchmark config.fullname:/Parse\/size=4k/
/{name-key}Sub-benchmark parameter/size:4k
/gomaxprocsGOMAXPROCS value/gomaxprocs:16
.fileInput file label.file:old.txt
.unitMetric unit.unit:sec/op
goosOS from headergoos:linux
goarchArchitecture from headergoarch:amd64
pkgPackage from headerpkg:/parser/
Filter examples
# Only Parse benchmarks
benchstat -filter '.name:Parse' old.txt new.txt

# Only benchmarks with size=4096 sub-parameter
benchstat -filter '/size:4096' old.txt new.txt

# Exclude Parallel benchmarks
benchstat -filter '-.name:/Parallel/' old.txt new.txt

# Linux amd64 only
benchstat -filter 'goos:linux goarch:amd64' old.txt new.txt

# Multiple benchmark names
benchstat -filter '.name:(Parse OR Encode OR Decode)' old.txt new.txt

# Complex: Linux or Darwin, not internal packages, only sec/op metric
benchstat -filter '(goos:linux OR goos:darwin) -pkg:/internal/ .unit:sec/op' old.txt new.txt

# Regex: all benchmarks starting with Bench
benchstat -filter '.name:/^Bench/' old.txt new.txt

Projection Examples

Default: before/after file comparison
benchstat old.txt new.txt
# Equivalent to:
benchstat -table .config -row .fullname -col .file old.txt new.txt

Creates one row per benchmark, one column per file.

Compare sub-benchmark parameters within a single file

When a single benchmark file contains multiple sub-benchmarks (e.g., BenchmarkEncode/format=json and BenchmarkEncode/format=gob):

benchstat -col /format bench.txt

Creates columns for each value of /format, comparing them against each other.

Simplify rows to base name only
benchstat -col /format -row .name bench.txt

Strips sub-benchmark configuration from row names, making the table more compact.

Control column order
# Force gob first, then json (instead of alphabetical)
benchstat -col '/format@(gob json)' bench.txt
Group by GOMAXPROCS
benchstat -col /gomaxprocs bench.txt

Compares performance across different GOMAXPROCS values within the same file.

Separate tables per package
benchstat -table pkg old.txt new.txt

Creates one table per package — useful when comparing benchmarks across multiple packages.

Ignore a dimension
# Suppress "benchmarks vary in /gomaxprocs" warning
benchstat -row .name -ignore /gomaxprocs bench.txt
Compare three versions
benchstat v1=v1.txt v2=v2.txt v3=v3.txt

Shows v2 vs v1 and v3 vs v1 (first input is always the base).

Cross-dimensional comparison
# Rows = benchmark name, columns = OS, separate tables per architecture
benchstat -row .name -col goos -table goarch results.txt

Unit Metadata

assume=exact

For metrics that should not vary between runs (e.g., binary size, generated code size):

BenchmarkSize 1 42 custom-bytes/op
Unit custom-bytes/op assume=exact

With assume=exact:

  • Non-parametric statistics are disabled

  • benchstat warns if measured values vary

  • Shows comparisons even with a single before/after measurement (no -count needed)

assume=nothing (default)

Standard behavior — uses non-parametric statistics (median + Mann-Whitney U-test). Requires multiple samples.

Interleaving Runs

Sequential runs (all old, then all new) are vulnerable to systematic bias — thermal throttling builds up over time, background processes come and go, CPU frequency scaling adapts. Interleaving reduces this:

# Pre-compile both versions to avoid measuring compilation time
go test -c -o old.test ./pkg/parser
# ... make your change ...
go test -c -o new.test ./pkg/parser

# Interleave runs — alternating reduces systematic bias
for i in $(seq 1 10); do
    ./old.test -test.bench=BenchmarkParse -test.benchmem >> old.txt
    ./new.test -test.bench=BenchmarkParse -test.benchmem >> new.txt
done

benchstat old.txt new.txt

Pre-compiling with go test -c is critical — without it, each go test -bench invocation includes compilation time, which varies and contaminates results.

How Many Runs?

ScenarioMinimum -countWhy
Quick local check6Enough for a rough confidence interval; fast feedback loop
Pre-merge comparison10Standard for detecting moderate (>5%) changes with confidence
Detecting small changes (<5%)20-30More samples narrow the CI; needed when signal is small relative to noise
Noisy CI environment20+Shared CI runners have higher variance; more runs compensate

Never "retry until significant" — rerunning benchmarks until ~ goes away introduces selection bias (p-hacking). If 10 runs show ~, the change is probably not meaningful. Increase run count once and accept the result.

At α=0.05, expect ~5% of benchmarks to randomly report significance with no real change (false positives). This is normal — don't chase them.

Single-File Summary

Analyze variance of a single run without comparison:

benchstat bench.txt

Shows median and confidence interval for each benchmark. Use to:

  • Check measurement stability before making code changes

  • Identify noisy benchmarks that need more runs or better isolation

  • Get a quick summary of current performance

Common Pitfalls

PitfallWhy it's wrongFix
-count=1Single run has no variance information; benchstat can't compute confidenceAlways use -count=6 minimum, prefer -count=10
Running on a laptop on batteryCPU throttles to save power; variance explodesPlug in, disable power saving, or use a desktop/server
Running with browser/IDE openBackground processes steal CPU cycles; adds noiseClose unnecessary applications, or accept wider CIs
Rerunning until ~ disappearsSelection bias (p-hacking) — you're cherry-picking runs that showed improvementRun once with high -count, accept the result
Comparing across machinesDifferent CPUs, memory, OS = incomparable baselinesSame machine, same conditions, both runs
Not interleavingSystematic bias from thermal throttling, background load driftPre-compile both versions with go test -c, alternate runs
Measuring compilation timego test -bench compiles first; startup overhead variesPre-compile with go test -c, run the binary directly
Ignoring wide CI (± >5%)Results look significant but variance is too high to be trustworthyFix the noise first, then compare; or increase -count
Comparing different -count valuesUnequal sample sizes bias the comparisonUse the same -count for all inputs

benchstat in CI

See CI Regression Detection for integrating benchstat comparisons into CI pipelines with benchdiff, cob, and gobenchdata.

Requirements and compatibility

Written for: Claude Code, OpenAI Codex

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Claude Code frontmatter

The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
samber
Publisher
Samuel Berthe (Community)
Distributed by
Samuel Berthe
Source
samber/cc-skills-golang · skills/golang-benchmark · commit bb1d43d
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-benchmark.zip