This page looks best with JavaScript enabled

Benchmarking in Go, with Example

 ·   ·  β˜• 3 min read

Introduction

From The Zen of Go:

If you think it’s slow, first prove it with a benchmark

Don’t assume if things are slow. Benchmark it and see if they are really slow.

One thing to note here is benchmarking a program is different from profiling a program.

Benchmarking is the way we check how fast our algorithm is for a given unit of the program. In benchmarking, we typically see how many iterations can a piece of code can run in a given time.

Profiling is more of a complete picture. Profiling can help determine which methods are called and how long each method takes to complete. Profiling also tell us a lot of valuable things, like what percentage of CPU time was use where, where memory was allocated and things like that. We can say benchmarking is part of profiling.

Today we’ll learn about benchmarking our function in Go.

How to benchmark a function in Go

In Go, benchmarking is closely tied to the testing suite. This literally means you’ll write a benchmarking code in the same place you write your unit/integration tests.

If you are familiar with unit tests in Go already, you know you can test a function with below command:

go test -run=FuncName

You can omit -run=FuncName part to run all the tests in the current level module. And you would benchmark a function the same function like this:

go test -bench=FuncName

Let us understand this with the help of an example. Below is main.go with DoSomeWork function.

1
2
3
4
5
// DoSomeWork is a example function which uses time.Sleep to
// sleep for an arbitrary number of time.
func DoSomeWork() {
    time.Sleep(time.Nanosecond * 500)
}

The above code is trivial self-documented, needs no explanation.

Now, just like test functions follow a naming rule of TestXXX (where XXX is the function name), the benchmark also follows the same naming semantic. Here is BenchmarkDoSomeWork in main_test.go:

1
2
3
4
5
func BenchmarkDoSomeWork(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DoSomeWork()
    }
}

How to interpret benchmark output

When you run the benchmark suite using go test -bench=ShareWith (or go test -bench=. for all benchmark in the current module).

You’ll see an output like:

BenchmarkDoSomeWork     535690              2337 ns/op

This means that the loop ran 4945279 times at a speed of 243 ns per loop.

Now let us optimize our DoSomeWork by decreasing the time it’s sleeping for:

1
2
3
func DoSomeWork() {
    time.Sleep(time.Nanosecond * 50)
}

The output again:

BenchmarkDoSomeWork     1362243               890 ns/op

As you can see, our program is taking less time per operation. Earlier it was 2337 nanosecond per operation and now it’s 890.

You might have noted that the number in the second column is inversely proportional to the number is the third column, which is obvious because when the function executes in less amount of time, it will be able to run more times in a certain time frame.

Conclusion

We have learned:

  • How to write benchmark tests for function.
  • How to read the output of the test.

If you require further assistance then please let me know in the comments section below!

Share on

Santosh Kumar
WRITTEN BY
Santosh Kumar
Fullstack Developer at Method Studios