Benchmarking your application is often a good idea when it comes for fine tuning its performance. The package contains a benchmarking facility that can be used to examine the performance of your code. In this article we'll see how to write simple benchmark tests that are able to provide us good insights about a given algorithmic solution. Golang testing Golang The good old Fibonacci number calculation is a classic numerical series where each subsequent number is the sum of the previous two numbers: 1 1 2 3 5 8 13... Fibonacci number Let's explore two different implementations: recursive and sequential. We'll write both unit and benchmark tests for each approach and then we'll be able to compare them. Recursive approach When you look at the , it seems to be very straightforward to implement in nearly any programming language. And probably the first approach to solve it is to use : Fibonacci algorithm recursion fibo { n <= { n } RecursiveFibonacci(n ) + RecursiveFibonacci(n ) } package func RecursiveFibonacci (n ) uint uint if 1 return return -1 -2 Each iteration in the series discards the previous results and then re-calculates the intermediate steps for each subsequent iteration. Let's add some unit tests: fibo { data := [] { n want }{ { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, } _, d := data { got := RecursiveFibonacci(d.n); got != d.want { t.Errorf( , got, d.want) } } } package import "testing" func TestRecursiveFibonacci (t *testing.T) struct uint uint 0 0 1 1 2 1 3 2 4 3 5 5 6 8 10 55 42 267914296 for range if "got: %d, want: %d" It works: tiago:~/develop/ /fibonacci/fibo$ test -run TestRecursiveFibonacci PASS ok bitbucket.org/tiagoharris/fibonacci/fibo go go 1.875s Sequential approach This alternative implementation removes the recursion and instead uses a simple for loop and a couple of variables. If you think about it, the algorithm is nothing but a sum of N numbers. We start from 0 and 1 and we will start adding subsequent sums: fibo { n <= { (n) } n2, n1 = , i := ( ); i < n; i++ { n2, n1 = n1, n1+n2 } n2 + n1 } package func SequentialFibonacci (n ) uint uint if 1 return uint var uint 0 1 for uint 2 return Let's add some unit tests: { data := [] { n want }{ { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, } _, d := data { got := SequentialFibonacci(d.n); got != d.want { t.Errorf( , got, d.want) } } } func TestSequentialFibonacci (t *testing.T) struct uint uint 0 0 1 1 2 1 3 2 4 3 5 5 6 8 10 55 42 267914296 for range if "got: %d, want: %d" It also works: tiago:~/develop/ /fibonacci/fibo$ test -run TestSequentialFibonacci PASS ok bitbucket.org/tiagoharris/fibonacci/fibo go go 0.631s Notice that we’ve got a considerable performance improvement here; 0.631s versus 1.875s. Benchmarking In order to measure performance, we could measure execution time and display it with some print statements, of course. But offers a very sophisticated tooling for benchmarking, and it's fairly simple to use. Golang Writing a benchmark is very similar to writing a test as they share the infrastructure from the testing package. Some of the key differences are: Benchmark functions start with ' ', not ' '; Benchmark Test Benchmark functions are run several times by the testing package. The value of 'b.N' will increase each time until the benchmark runner is satisfied with the stability of the benchmark; Each benchmark must execute the code under test b.N times. Thus, a 'for' loop will be present in every benchmark function. Our final file will contain both unit and benchmark tests: fibo_test.go fibo ( ) { i := ; i < b.N; i++ { RecursiveFibonacci( ) } } { i := ; i < b.N; i++ { RecursiveFibonacci( ) } } { i := ; i < b.N; i++ { SequentialFibonacci( ) } } { i := ; i < b.N; i++ { SequentialFibonacci( ) } } { data := [] { n want }{ { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, } _, d := data { got := RecursiveFibonacci(d.n); got != d.want { t.Errorf( , got, d.want) } } } { data := [] { n want }{ { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, { , }, } _, d := data { got := SequentialFibonacci(d.n); got != d.want { t.Errorf( , got, d.want) } } } package import "testing" func BenchmarkTestRecursiveFibonacci_10 (b *testing.B) for 0 10 func BenchmarkTestRecursiveFibonacci_20 (b *testing.B) for 0 20 func BenchmarkTestSequentialFibonacci_10 (b *testing.B) for 0 10 func BenchmarkTestSequentialFibonacci_20 (b *testing.B) for 0 20 func TestRecursiveFibonacci (t *testing.T) struct uint uint 0 0 1 1 2 1 3 2 4 3 5 5 6 8 10 55 42 267914296 for range if "got: %d, want: %d" func TestSequentialFibonacci (t *testing.T) struct uint uint 0 0 1 1 2 1 3 2 4 3 5 5 6 8 10 55 42 267914296 for range if "got: %d, want: %d" We'll benchmark both recursive and sequential approaches by calculating the sequence for 10 and 20. With benchmark tests in place, all we need to do is to invoke it via "go test -bench=.". By default, it runs using all the CPUs available. You can change like this: "go test -cpu=4 -bench=.". My machine has 8 CPUs, as we can see by running : htop Lets run it: tiago:~/develop/ /fibonacci/fibo$ test -bench=. goos: darwin goarch: amd64 pkg: bitbucket.org/tiagoharris/fibonacci/fibo cpu: Intel(R) Core(TM) i7 HQ CPU @ GHz BenchmarkTestRecursiveFibonacci_10 ns/op BenchmarkTestRecursiveFibonacci_20 ns/op BenchmarkTestSequentialFibonacci_10 ns/op BenchmarkTestSequentialFibonacci_20 ns/op PASS ok bitbucket.org/tiagoharris/fibonacci/fibo go go -7820 2.90 -8 3534949 335.2 -8 28592 41587 -8 372993714 3.221 -8 193414836 6.175 8.406s The output format is: Benchmark< -name>-<number-of-cpus> number of executions speed of each operation test Now we can have a better idea of how the sequential approach is way more efficient than the recursive one: was executed 3,534.949 times with a speed of 335.2 ns/op, while was executed 372,993.714 times with a speed of 3.221 ns/op; BenchmarkTestRecursiveFibonacci10-8 BenchmarkTestSequentialFibonacci10-8 was executed 28,592 times with a speed of 41730 ns/op, while was executed 193,414.836 with a speed of 6.175 ns/op. BenchmarkTestRecursiveFibonacci20-8 BenchmarkTestSequentialFibonacci20-8 Plotting graphics I'm a huge fan of . I've even written an showing how it can be useful. gnuplot article This is the file that will be used to plot a : gnuplot box graphic terminal png grid output graphic_file_name title datafile separator key off ylabel y_label yrange[y_range_min:y_range_max] format y xtics rotate style fill solid boxwidth 0.5 plot [ =0:*] file_path every ::i::i using column_1:column_2:xtic(2) with boxes ## # gnuplot script to generate a performance graphic. # # it expects the following parameters: # # file_path - path to the file from which the data will be read # graphic_file_name - the graphic file name to be saved # y_label - the desired label for y axis # y_range_min - minimum range for values in y axis # y_range_max - maximum range for values in y axis # column_1 - the first column to be used in plot command # column_2 - the second column to be used in plot command # # Author: Tiago Melo (tiagoharris@gmail.com) ## # graphic will be saved as 800x600 png image file set # allows grid lines to be drawn on the plot set # setting the graphic file name to be saved set # the graphic's main title set "performance comparison" # since the input file is a CSV file, we need to tell gnuplot that data fields are separated by comma set "," # disable key box set # label for y axis set # range for values in y axis set # to avoid displaying large numbers in exponential format set "%.0f" # vertical label for x values set # set boxplots set set # plot graphic for each line of input file for i This is the target in our that runs the benchmark tests and plot graphics for both number of operations and speed of each operation, so we can easily compare them: benchmark Makefile benchmark: @ fibo ; \ go -bench=. | tee ../graphic/out.dat ; \ awk ../graphic/out.dat > ../graphic/final.dat ; \ gnuplot -e -e -e -e -e -e -e ../graphic/performance.gp ; \ gnuplot -e -e -e -e -e -e -e ../graphic/performance.gp ; \ rm -f ../graphic/out.dat ../graphic/final.dat ; \ cd test '/Benchmark/{count ++; gsub(/BenchmarkTest/,""); printf("%d,%s,%s,%s\n",count,$$1,$$2,$$3)}' "file_path='../graphic/final.dat'" "graphic_file_name='../graphic/operations.png'" "y_label='number of operations'" "y_range_min='000000000''" "y_range_max='400000000'" "column_1=1" "column_2=3" "file_path='../graphic/final.dat'" "graphic_file_name='../graphic/time_operations.png'" "y_label='each operation in nanoseconds'" "y_range_min='000''" "y_range_max='45000'" "column_1=1" "column_2=4" echo "'graphic/operations.png' and 'graphic/time_operations.png' graphics were generated." First, runs the benchmark tests using a with command, which makes it possible to both display the output in the terminal & save it to a file. pipe tee Then, we use command to parse our file into a format that will be used to plot the graphics. It looks like this: awk CSV ,RecursiveFibonacci_10 , , ,RecursiveFibonacci_20 , , ,SequentialFibonacci_10 , , ,SequentialFibonacci_20 , , 1 -8 3579872 334.3 2 -8 29028 42352 3 -8 375031484 3.238 4 -8 195996889 6.148 Next, we call two times: 1) generate graphic for number of executions 2) generate graphic for speed of each operation. gnuplot Let's run it: tiago:~/develop/go/fibonacci$ make benchmark goos: darwin goarch: amd64 pkg: bitbucket.org/tiagoharris/fibonacci/fibo cpu: Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz BenchmarkTestRecursiveFibonacci_10-8 3579872 334.3 ns/op BenchmarkTestRecursiveFibonacci_20-8 29028 42352 ns/op BenchmarkTestSequentialFibonacci_10-8 375031484 3.238 ns/op BenchmarkTestSequentialFibonacci_20-8 195996889 6.148 ns/op PASS ok bitbucket.org/tiagoharris/fibonacci/fibo 8.844s and graphics were generated. 'graphic/operations.png' 'graphic/time_operations.png' Awesome. Number of operations: Speed of each operation: Pretty cool, isn't it? Bonus: calculation of large Fibonacci numbers The first idea that comes to my mind would be to use 128-bit integer variable. Unfortunately, Go does not have one (yet). But even then, there is one of the numbers that will not fit into 128-bit integer and we would need 256-bit integer and so on. Fortunately, Go has a package called and its type that will be very handy in this implementation: Fibonacci math/big Int { n <= { big.NewInt( (n)) } n2, n1 = big.NewInt( ), big.NewInt( ) i := ( ); i < n; i++ { n2.Add(n2, n1) n1, n2 = n2, n1 } n1 } * . func SequentialFibonacciBig (n ) uint big Int if 1 return int64 var 0 1 for uint 1 return To test it, here's our that accepts the desired number as a parameter: main.go main ( ) { n flag.Uint64Var(&n, , , ) flag.Parse() fmt.Printf( , n, fibo.SequentialFibonacciBig( (n))) } package import "flag" "fmt" "bitbucket.org/tiagoharris/fibonacci/fibo" func main () var uint64 "n" 0 "n" "%d: %d\n" uint And here's our target in to run it: Makefile @ go build -a -installsuffix cgo -o main . @ if [ -z ]; then echo >&2 please set the number via the variable N; exit 2; fi ## build: build app's binary build: ## run: run the app run: build " " $(N) Let's run it for, say, 200: tiago:~/develop/go/fibonacci$ make run N=200 200: 280571172992510140037611932413038677189525 Conclusion In this article we learned how to use benchmark utility and how to use to plot graphics for a better comparison. Golang testing gnuplot Download the source Here: https://bitbucket.org/tiagoharris/fibonacci/src/master/