Benchmarks
JavaScript benchmarking vs Go benchmarking
Section titled “JavaScript benchmarking vs Go benchmarking”In JavaScript you reach for console.time / console.timeEnd for quick measurements, or benchmark.js for statistically rigorous micro-benchmarks. Both run outside the test runner and require separate setup.
In Go, benchmarks live in the same _test.go files as unit tests. You run them with the same go test command — just add a flag. The toolchain handles statistical warmup automatically.
Playground note: Benchmarks require the
go test -benchbinary context. The code blocks below are real benchmark files — run them locally.
The benchmark function signature
Section titled “The benchmark function signature”// JavaScript — benchmark.js (external library)import Benchmark from 'benchmark';
const suite = new Benchmark.Suite();suite .add('sum-loop', () => { let total = 0; for (let i = 0; i < 1000; i++) total += i; }) .on('complete', function () { console.log(this[0].toString()); }) .run();// Go — built into testing package// sum_test.gopackage main
import "testing"
func sum(n int) int { total := 0 for i := 0; i < n; i++ { total += i } return total}
func BenchmarkSum(b *testing.B) { for b.Loop() { // Go 1.24+: b.Loop() is the preferred form sum(1000) }}Running benchmarks
Section titled “Running benchmarks”# Run all benchmarks in the current packagego test -bench=. ./...
# Run benchmarks matching a regexgo test -bench=BenchmarkSum ./...
# Also run unit tests alongside benchmarksgo test -bench=. -run=. ./...
# Skip unit tests (run benchmarks only)go test -bench=. -run='^$' ./...
# Control benchmark time (default 1s)go test -bench=. -benchtime=5s ./...
# Report memory allocationsgo test -bench=. -benchmem ./...Reading benchmark output
Section titled “Reading benchmark output”BenchmarkSum-8 14253187 84.23 ns/opBenchmarkSum-8 14253187 84.23 ns/op 0 B/op 0 allocs/op| Column | Meaning |
|---|---|
BenchmarkSum-8 | Benchmark name + GOMAXPROCS (CPU count) |
14253187 | Number of iterations (b.N) the runner chose |
84.23 ns/op | Nanoseconds per operation |
0 B/op | Bytes allocated per operation (-benchmem) |
0 allocs/op | Heap allocations per operation (-benchmem) |
The Go runner increases b.N until the result is statistically stable — you never hard-code the iteration count.
Comparing implementations
Section titled “Comparing implementations”package strings_test
import ( "strings" "testing")
func BenchmarkConcatPlus(b *testing.B) { for b.Loop() { var s string for i := 0; i < 100; i++ { s += "x" // allocates on every iteration } _ = s }}
func BenchmarkConcatBuilder(b *testing.B) { for b.Loop() { var sb strings.Builder for i := 0; i < 100; i++ { sb.WriteByte('x') // single pre-allocated buffer } _ = sb.String() }}Run with -benchmem to see how drastically allocations differ:
BenchmarkConcatPlus-8 228468 5213 ns/op 5440 B/op 99 allocs/opBenchmarkConcatBuilder-8 2134500 562 ns/op 128 B/op 1 allocs/opProfiling during benchmarks
Section titled “Profiling during benchmarks”# Write a CPU profile while benchmarkinggo test -bench=BenchmarkSearch -cpuprofile=cpu.out ./...
# Write a heap (memory) profilego test -bench=BenchmarkSearch -memprofile=mem.out ./...
# Open the profile in the interactive pprof toolgo tool pprof cpu.outThe pprof tool is covered in depth in the pprof Profiling lesson.