hrtime: Benchmarking with Details
I’ve been optimizing Go code for a while and trying to improve my benchmarking game.
Let’s take a look at a simple example:
|
|
Running go test -bench .
will give us the result:
|
|
This might be enough for getting a rough estimate where you stand in terms of performance, but a more detailed output is required for optimization. Condensing everything into a single number is bound to be simplistic.
Let me introduce you to the hrtime package which I wrote to get a more extensive output from benchmarks.
Histogram #
The first approach to using it is to use hrtime.NewBenchmark
. Rewriting the first example as a regular program.
|
|
Which will print:
|
|
As we can see that P99 is 500ns, which means that 1% of all our measurements are above 500ns. We can try to optimize this by allocating fewer strings:
|
|
And the result:
|
|
We can now see that the 99% has gone from 500ns to 300ns.
If you have a keen eye, you may have noticed that Go benchmark gave an average 107ns/op
however hrtime
gave us 372ns/op
. This is the unfortunate side-effect in trying to measure more – it always has an overhead. The final results include this overhead.
Note: depending on the operating-system the overhead can be significantly less and hrtime
does support repeated calls inside with https://godoc.org/github.com/loov/hrtime#Histogram.Divide.
Stopwatch #
Sometimes you also want to measure concurrent operations. For this there is Stopwatch
. https://godoc.org/github.com/loov/hrtime#Stopwatch
Let’s say you want to measure how long does a send take on a highly contended channel. This, of course, is a contrived example, but roughly shows the idea of how we can start the measurement from one goroutine, stop it in another and finally print it all out.
|
|
hrtesting #
Of course, writing a separate binary for all of the tests isn’t that convenient. For that there’s **github.com/loov/hrtime/hrtesting**
, which provides wrappers for testing.B
.
|
|
It will print out 50%, 90%, and 99% percentiles.
|
|
Unfortunately, with Go 1.12 it will print all of the runs of Benchmark instead of just the last. However, with Go 1.13, the output will be much nicer:
|
|
And comparing results with benchstat will work as well.
hrplot #
Leaving the best for last github.com/loov/hrtime/hrplot. Using my experimental plotting package, I decided to add a convenient way to plot the benchmark results.
|
|
Will create an SVG file called all.svg
. It contains a line plot, which shows how much time each iteration took. Second is a density plot, which shows the distribution of timing measurements. And finally a percentiles plot.
Conclusion #
Performance optimization is fun, but having tools to help you out makes it even more enjoyable.
Go try out github.com/loov/hrtime and let me know what you think.