Skip to content

Go Benchmarking — Performance Testing with testing.B and Benchmark Functions

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Go Benchmarking. We cover key concepts, practical examples, and best practices to help you master this topic.

Go benchmarking measures performance with testing.B for timing, b.N for iterations, and benchstat for statistical comparison of benchmark results.

What You'll Learn

  • Writing benchmarks
  • Benchmark comparison
  • Memory allocation profiling
  • Benchmark best practices

Why It Matters

Benchmarks prevent performance regressions. Docker benchmarks build performance. Kubernetes benchmarks scheduler latency. DodaZIP benchmarks compression throughput.

Real-World Use

Performance regression detection, optimization validation, library comparison, capacity planning.

flowchart LR
    A["Benchmarking"] --> B["Benchmark Functions"]
    A --> C["Memory Profiling"]
    A --> D["benchstat"]
    A --> E["Optimization"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Benchmark

func BenchmarkStringConcat(b *testing.B) {
    a, c := "hello", "world"
    for i := 0; i < b.N; i++ {
        _ = a + " " + c
    }
}

Memory Allocations

func BenchmarkStringBuilder(b *testing.B) {
    a, c := "hello", "world"
    for i := 0; i < b.N; i++ {
        var sb strings.Builder
        sb.WriteString(a)
        sb.WriteString(" ")
        sb.WriteString(c)
        _ = sb.String()
    }
}

// Run with: go test -bench=. -benchmem

Comparing Implementations

var testData = make([]int, 10000)

func init() {
    for i := range testData { testData[i] = i }
}

func BenchmarkSumLoop(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var sum int
        for _, v := range testData { sum += v }
        _ = sum
    }
}

func BenchmarkSumReduce(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = Reduce(testData, 0, func(a, b int) int { return a + b })
    }
}

Parallel Benchmark

func BenchmarkParallel(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            // Parallel operation
            compute()
        }
    })
}

Sub-Benchmarks

func BenchmarkSort(b *testing.B) {
    sizes := []int{10, 100, 1000, 10000}
    for _, size := range sizes {
        b.Run(fmt.Sprintf("n=%d", size), func(b *testing.B) {
            data := make([]int, size)
            for i := 0; i < b.N; i++ {
                b.StopTimer()
                for j := range data { data[j] = size - j }
                b.StartTimer()
                sort.Ints(data)
            }
        })
    }
}

Reset Timer

func BenchmarkWithSetup(b *testing.B) {
    data := generateLargeData()
    b.ResetTimer()

    for i := 0; i < b.N; i++ {
        process(data)
    }
}

Common Mistakes

1. Compiler Optimizations

// Bad: result never used, compiler may eliminate
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ { add(3, 4) }
}
// Good: assign result to package-level variable
var result int
func BenchmarkAdd(b *testing.B) {
    var r int
    for i := 0; i < b.N; i++ { r = add(3, 4) }
    result = r
}

2. Setup Inside Loop

Move setup code outside the benchmark loop or use b.ResetTimer.

3. Not Using benchstat

Run benchmarks multiple times: go test -bench=. -count=10 > old.txt. Compare with benchstat.

4. Forgetting -benchmem

Always benchmark with -benchmem to measure allocations and bytes per operation.

5. Starting Timer Too Early

Include setup time. Use b.ResetTimer() after setup. Use b.StopTimer/b.StartTimer for expensive setup.

Practice Questions

1. What does b.N control? The number of iterations. Go determines b.N automatically to get stable timing (usually 1ms+ of runtime).

2. How do you measure allocations? Use go test -bench=. -benchmem. Output shows allocs/op and bytes/op.

3. What is benchstat? A tool for statistical comparison of benchmark results. go install golang.org/x/perf/cmd/benchstat@latest.

4. Why assign results to a global variable? Prevents compiler from eliminating dead code. Without it, the benchmarked operation may be optimized away.

Challenge: Benchmark two implementations of JSON marshal: struct vs map.

Solution
type Data struct {
    Name  string `json:"name"`
    Value int    `json:"value"`
}

func BenchmarkMarshalStruct(b *testing.B) {
    d := Data{Name: "test", Value: 42}
    for i := 0; i < b.N; i++ {
        json.Marshal(d)
    }
}

func BenchmarkMarshalMap(b *testing.B) {
    m := map[string]interface{}{"name": "test", "value": 42}
    for i := 0; i < b.N; i++ {
        json.Marshal(m)
    }
}

FAQ

{{< faq question="How many iterations should I run?" >}} Let Go decide with b.N. It automatically adjusts for stable timing. Use -benchtime=5s for longer runs. {{< /faq >}}

{{< faq question="What is a good benchmark precision?" >}} Run -count=10 and use benchstat. Benchmarks are noisy — multiple runs give statistical significance. {{< /faq >}}

{{< faq question="How do I benchmark memory only?" >}} Use -benchmem flag. Also try -bench=BenchmarkAlloc targeting allocation-heavy functions. {{< /faq >}}

{{< faq question="Can I compare benchmarks across machines?" >}} Not meaningfully. Use relative comparison (before/after change) on the same machine. Normalize to operation count. {{< /faq >}}

{{< faq question="What is the difference between B and T?" >}} testing.B for benchmarks (measures performance). testing.T for tests (checks correctness). Both share common methods. {{< /faq >}}

Try It Yourself

package main

import "testing"

func BenchmarkHello(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = "Hello, World!"
    }
}

Run with:

go test -bench=. -benchmem

Expected output — benchmark result with ns/op, B/op, allocs/op.

What's Next

Now that you understand benchmarking, explore profiling for deep performance analysis.

Topic Description Link
Go Profiling Performance profiling {{< ref "36-profiling" >}}
Go Testing Advanced Advanced testing {{< ref "34-testing-advanced" >}}
Go Modules Package management {{< ref "37-modules" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro

Home Browse Go