You wrote a function that works. Now you want to know if it’s fast enough. Go’s built-in benchmarking runs your code millions of times and tells you exactly how many nanoseconds each operation takes.
No external tools. No frameworks to install. Just add Benchmark to your test file and run one command.
You already know how to write tests. Benchmarks use the same file, same package, similar structure.
hello_test.go
package main
import "testing"
func Hello(name string) string {
return "Hello, " + name
}
func BenchmarkHello(b *testing.B) {
for i := 0; i < b.N; i++ {
Hello("World")
}
} Three things to notice:
Benchmark*testing.B instead of *testing.Tb.N times in a loopRun go test -bench=.:
BenchmarkHello-8 50000000 24.3 ns/op
PASS Your function ran 50 million times. Each call took 24.3 nanoseconds. Go figured out how many times to run it automatically.
Your function is fast. To measure something that takes 24 nanoseconds, Go needs to run it many times to get an accurate average. Fast functions run millions of times. Slower functions run fewer times.
You don’t control b.N. Go adjusts it until the benchmark runs for about 1 second total. That’s how it gets reliable measurements.
You can write multiple benchmarks to compare different ways of doing the same thing. Here’s string concatenation two different ways:
func HelloWithPlus(name string) string {
return "Hello, " + name
}
func HelloWithSprintf(name string) string {
return fmt.Sprintf("Hello, %s", name)
}
func BenchmarkHelloWithPlus(b *testing.B) {
for i := 0; i < b.N; i++ {
HelloWithPlus("World")
}
}
func BenchmarkHelloWithSprintf(b *testing.B) {
for i := 0; i < b.N; i++ {
HelloWithSprintf("World")
}
} Run go test -bench=.:
BenchmarkHelloWithPlus-8 50000000 24.3 ns/op
BenchmarkHelloWithSprintf-8 10000000 112 ns/op
PASS The + operator is about 5x faster than fmt.Sprintf. You learned this by measuring, not guessing.
BenchmarkHello-8 50000000 24.3 ns/op Breaking this down:
BenchmarkHello: Your function name-8: Number of CPU cores used50000000: How many times it ran24.3 ns/op: Average nanoseconds per operationThe important number is ns/op. Lower is faster.
Add -benchmem to see how much memory your code allocates:
go test -bench=. -benchmem BenchmarkHelloWithPlus-8 50000000 24.3 ns/op 13 B/op 1 allocs/op
BenchmarkHelloWithSprintf-8 10000000 112 ns/op 21 B/op 2 allocs/op
PASS New columns:
13 B/op: Bytes allocated per operation1 allocs/op: Number of allocations per operationFewer allocations usually means less work for the garbage collector.
go test -bench=. # Run all benchmarks
go test -bench=Hello # Run benchmarks matching "Hello"
go test -bench=. -benchmem # Include memory statistics You don’t need to benchmark everything. Write benchmarks when:
Don’t guess about performance. Measure it.
After writing a few benchmarks, you’ll start noticing patterns. String concatenation with + is faster than fmt.Sprintf for simple cases. Creating maps has overhead. Some things you think are slow are actually fast, and vice versa.
Benchmarks teach you which Go patterns are fast and which are slow. You learn by measuring your actual code, not memorizing rules.
Start with a function you’ve already written. Add a benchmark. Run it. See what you learn. The numbers might surprise you.