Stage 3 · Build
Metrics, Health & Profiling
Benchmark Analysis
go test -bench, benchstat, allocation counts, and isolating noisy measurements.
Benchmark Basics
Go benchmarks measure code performance. The testing framework adjusts b.N to get stable measurements. Use -benchmem to include allocation data.
benchstat Analysis
# Install benchstat
go install golang.org/x/perf/cmd/benchstat@latest
# Save baseline
go test -bench=. -benchmem -count=10 > old.txt
# Make changes, save new results
go test -bench=. -benchmem -count=10 > new.txt
# Compare
benchstat old.txt new.txt
# Output:
# name old time/op new time/op delta
# Parse-8 1.23µs ± 2% 0.98µs ± 1% -20.33% (p=0.000 n=10+10)
# Marshal-8 2.45µs ± 3% 2.41µs ± 2% ~ (p=0.142 n=10+10)
# Filter by name
benchstat -filter 'Parse' old.txt new.txt
# Compare specific benchmarks
benchstat -col old old.txt new.txt
# HTML report
benchstat -html old.txt new.txt > report.htmlbenchstat requires multiple runs (count=10) for statistical analysis. The p-value indicates significance — p < 0.05 is statistically significant. delta shows the percentage change. Filter focuses on specific benchmarks.
Allocation Counts
Isolating Noise
# Run many iterations for stability
go test -bench=. -benchmem -count=20
# Pin CPU frequency (Linux)
sudo cpupower frequency-set -g performance
# Disable turbo boost
echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo
# Close other applications
# Stop unnecessary services
# Use a dedicated benchmark machine
# Use benchstat's statistical analysis
benchstat -count 10 old.txt new.txt
# Check for outliers
go test -bench=. -benchmem -count=50 | sort -k3 -nNoise comes from CPU frequency scaling, turbo boost, other processes, and GC. Pin CPU frequency, disable turbo boost, and run on a dedicated machine. benchstat's statistical analysis handles remaining noise.
Benchmark Subtests
CI Benchmarks
name: Benchmark
on:
pull_request:
paths: ['**/*.go']
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Save baseline
run: |
git checkout main
go test -bench=. -benchmem -count=5 -timeout=10m > old.txt
- name: Save PR results
run: |
git checkout -
go test -bench=. -benchmem -count=5 -timeout=10m > new.txt
- name: Compare
run: |
go install golang.org/x/perf/cmd/benchstat@latest
benchstat old.txt new.txt
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const output = require('child_process')
.execSync('benchstat old.txt new.txt')
.toString();
github.rest.issues.createComment({
issue_number: context.issue.number,
body: `## Benchmark Results\n\n${output}`
});CI benchmarks compare PR changes against the main branch. Save baseline from main, compare with PR results. Post results as PR comments for visibility. Fail on significant regressions.
Performance regressions are easiest to catch and fix when they are introduced. Run benchmarks on every PR, compare with the baseline, and fail on regressions. This creates a culture of performance awareness.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.