How to Write and Run Benchmarks in Go

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setting up the Environment
  4. Writing Benchmarks
  5. Running Benchmarks
  6. Analyzing Benchmark Results
  7. Conclusion

Introduction

In this tutorial, we will learn how to write and run benchmarks in Go. Benchmarks are essential for measuring the performance of your code and identifying potential bottlenecks. By the end of this tutorial, you will be able to write benchmark functions, run them using Go’s built-in benchmarking tool, and analyze the benchmark results.

Prerequisites

To follow along with this tutorial, you should have a basic understanding of the Go programming language. You should also have Go installed on your computer. If you haven’t installed Go yet, please visit the official Go website (https://golang.org) and follow the installation instructions for your operating system.

Setting up the Environment

Before we start writing benchmarks, let’s set up our development environment. Open your terminal or command prompt and create a new directory for our project:

mkdir benchmark-example
cd benchmark-example

Next, create a new Go module inside the project directory to manage our dependencies:

go mod init benchmark-example

This will create a go.mod file in the project directory.

Writing Benchmarks

  1. Create a new file called benchmark_test.go inside the project directory:
     touch benchmark_test.go
    
  2. Open benchmark_test.go in your favorite text editor.

  3. Add the following import statement to the top of the file:
     import "testing"
    
  4. Below the import statement, write a benchmark function. The naming convention for benchmark functions is BenchmarkXxx, where Xxx is a descriptive name for your benchmark.

     func BenchmarkAddition(b *testing.B) {
         for i := 0; i < b.N; i++ {
             result := 2 + 2
             // Do something with the result to avoid compiler optimizations
             if result == 5 {
                 b.Fatal("This should never happen")
             }
         }
     }
    

    In this example, our benchmark function BenchmarkAddition performs the addition of two numbers and repeats it b.N times. The variable b of type *testing.B provides methods to manage the benchmark execution.

  5. Save the benchmark_test.go file.

Running Benchmarks

To run the benchmarks, open your terminal or command prompt and navigate to the project directory.

Execute the following command:

go test -bench=.

The -bench flag specifies the pattern to match benchmarks. In this case, . matches all benchmarks in the current directory.

You should see an output similar to the following:

goos: darwin
goarch: amd64
pkg: benchmark-example
BenchmarkAddition-8   	1000000000	         0.370 ns/op
PASS
ok  	benchmark-example	0.381s

The benchmark result indicates the time it took to execute the benchmarked function. Here, BenchmarkAddition-8 means the benchmark was executed using 8 goroutines, and 1000000000 indicates that the benchmark was executed 1,000,000,000 times with an average time of 0.370 nanoseconds per operation.

Analyzing Benchmark Results

By default, Go’s benchmarking tool provides basic statistics about the benchmark results. However, we can enable more detailed output using the -benchmem flag.

Update the go test command as follows:

go test -bench=. -benchmem

You should see an output similar to the following:

BenchmarkAddition-8   	1000000000	         0.370 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	benchmark-example	0.386s

The B/op column represents the number of bytes allocated per operation, and allocs/op represents the number of allocations per operation. These metrics can help identify memory allocation issues and optimize the memory usage of your code.

Conclusion

In this tutorial, you have learned how to write and run benchmarks in Go. You have also seen how to analyze the benchmark results to identify performance bottlenecks and memory allocation issues. Remember to write meaningful benchmarks for relevant parts of your codebase to ensure the best performance possible. Happy benchmarking!

Frequently Asked Questions

Q: Can I benchmark multiple functions in a single test file? A: Yes, you can define multiple benchmark functions in a single test file. Simply follow the naming convention BenchmarkXxx for each function.

Q: How can I increase the number of benchmark iterations? A: By default, Go’s benchmarking tool runs benchmarks for a duration that provides stable results. However, you can increase the number of iterations by modifying the loop condition in your benchmark function.

Q: How do I control the number of goroutines used for benchmarking? A: The number of goroutines used for benchmarking is determined by the -cpu flag when running the benchmarks. It is also controlled by the GOMAXPROCS environment variable.