Go Benchmarking: Measure, Don't Guess
Go's benchmark runner finds stable performance numbers by repeatedly calling your code in a loop controlled by b.N. Use it to optimize hot paths or compare algorithm implementations. Forgetting b.ResetTimer() will include setup costs, skewing your results.
WHY IT EXISTS: To provide a standardized, statistically sound way to measure code performance. Simple manual timing is often misleading because it doesn't account for system noise, garbage collection pauses, or other runtime effects. The Go toolchain provides this framework to get reliable, repeatable measurements.
THE MENTAL MODEL: Think of go test -bench as a scientist running an experiment. Your benchmark function is the experiment, and the Go runtime is the scientist trying to get a stable result. It runs your code in a loop, starting with a small number of iterations (b.N) and automatically increasing it until the time per operation becomes statistically stable. Your only job is to wrap the code you want to measure in for i := 0; i < b.N; i++.
HOW IT WORKS: You write a function with the signature func BenchmarkXxx(b *testing.B) in a file ending with _test.go. Inside this function, perform any necessary setup. If this setup is expensive and should not be part of the measurement, call b.ResetTimer() immediately after it. Then, write the for i := 0; i < b.N; i++ loop containing the code to be benchmarked. Run your benchmarks from the terminal using go test -bench=.. To also see memory allocations, add the -benchmem flag.
WHEN TO USE IT: Use it when performance is a critical requirement. It's ideal for optimizing a CPU-bound hot path, quantitatively comparing two different algorithms for the same task, or establishing a performance baseline that can be checked in CI to prevent regressions.
WHEN NOT TO USE IT: Benchmarks are less effective for measuring operations dominated by external factors with high variability, like network requests or disk I/O. The noise from the external system will overwhelm the measurement of your code's execution time. For these scenarios, distributed tracing and production profiling are more appropriate tools.
ONE CANONICAL EXAMPLE: To compare the performance of fmt.Sprintf versus strings.Builder for string construction, you would write two benchmark functions. The Sprintf benchmark would simply loop b.N times calling fmt.Sprintf. The Builder benchmark would create the strings.Builder once before the loop, call b.ResetTimer() to exclude that setup cost, and then the loop would perform the WriteString operations. This correctly isolates the performance of the repeated action.
Read the original → pkg.go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.