Hello Gophers đź‘‹

Let me start by asking you a question: How would you test the performance of a piece of code or a function in Go? Well, you could use **benchmark** tests.

In this tutorial, I will show you how to use an awesome benchmarking tool thatâ€™s built into the Golang testing package.

Letâ€™s go.

## What Are Benchmark Tests?

In Go, benchmark tests are used to measure the performance (speed and memory usage) of functions or blocks of code. These tests are part of the Go testing framework and are written in the same files as unit tests, but they are specifically for performance analysis.

## Example Use Case: Fibonacci Sequence

For this example, I'll be using the classic Fibonacci Sequence, which is determined by:

```
if (x < 2)
F(0) = 1
F(2) = 2
else
F(x) = F(x-1) + F(x-2)
In practice, the sequence is:
1, 1, 2, 3, 5, 8, 13, etc.
```

This sequence is important because it appears in various parts of mathematics and nature as well, as shown below:

There are several ways to implement this code, and I'll be picking two of them for our benchmark testing: the recursive and iterative methods. The main objective of the functions is to provide a *position* and return the Fibonacci number at that position.

### Recursive Method

```
// main.go
func fibRecursive(n uint) uint {
if n <= 2 {
return 1
}
return fibRecursive(n-1) + fibRecursive(n-2)
}
```

The function above is a recursive implementation of calculating the Fibonacci sequence. Now Iâ€™ll break it down step by step for you as a beginner in Go.

Hereâ€™s your function for calculating the Fibonacci numbers:

```
func fibRecursive(n uint) uint {
if n <= 2 {
return 1
}
return fibRecursive(n-1) + fibRecursive(n-2)
}
```

#### 1. **Function:**

```
func fibRecursive(n uint) uint
```

`func`

: This keyword defines a function in Go.`fibRecursive`

: This is the name of the function. Itâ€™s called`fibRecursive`

because it calculates Fibonacci numbers using recursion.`n uint`

: The function takes a single argument,`n`

, which is of type`uint`

(an unsigned integer). This represents the position of the Fibonacci sequence that we want to calculate.`uint`

: The function returns a`uint`

(unsigned integer) because Fibonacci numbers are non-negative integers.

#### 2. **Base Stage:**

```
if n <= 2 {
return 1
}
```

The

`if`

statement checks if`n`

is less than or equal to 2.In the Fibonacci sequence, the 1st and 2nd numbers are both 1. So, if

`n`

is 1 or 2, the function returns 1.This is called the

**base stage,**and it stops the recursion from going infinitely deep.

#### 3. **Recursive Stage:**

```
return fibRecursive(n-1) + fibRecursive(n-2)
```

If

`n`

is greater than 2, the function calls itself twice:`fibRecursive(n-1)`

: This will calculate the Fibonacci number for the position just before`n`

.`fibRecursive(n-2)`

: This will calculate the Fibonacci number for two positions before`n`

.

The function then adds these two results together, because every Fibonacci number is the sum of the two preceding numbers.

For more theory on recursion, check out these articles.

### Iterative Method

```
// main.go
func fibIterative(position uint) uint {
slc := make([]uint, position)
slc[0] = 1
slc[1] = 1
if position <= 2 {
return 1
}
var result, i uint
for i = 2; i < position; i++ {
result = slc[i-1] + slc[i-2]
slc[i] = result
}
return result
}
```

This code implements an **iterative** approach to calculate the Fibonacci sequence in Go, which is different from the **recursive** approach. Hereâ€™s a breakdown of how it works:

#### 1. **Function:**

```
func fibIterative(position uint) uint
```

`func`

: This keyword declares a function in Go.`fibIterative`

: The name of the function suggests that it calculates Fibonacci numbers using iteration (a loop).`position uint`

: The function takes one argument,`position`

, which is an unsigned integer (`uint`

). This represents the position of the Fibonacci sequence you want to calculate.`uint`

: The function returns an unsigned integer (`uint`

), which will be the Fibonacci number at the specified position.

#### 2. **Creating a Slice (Array-like structure):**

```
slc := make([]uint, position)
```

`slc`

is a slice (a dynamic array in Go) that is created with the length of`position`

. This slice will store Fibonacci numbers at each index.

#### 3. **Initial Values for Fibonacci Sequence:**

```
slc[0] = 1
slc[1] = 1
```

- The first two Fibonacci numbers are both
`1`

, so the first two positions in the slice (`slc[0]`

and`slc[1]`

) are set to`1`

.

#### 4. **Early Return for Small Positions:**

```
if position <= 2 {
return 1
}
```

- If the input
`position`

is`1`

or`2`

, the function directly returns`1`

, because the first two Fibonacci numbers are always`1`

.

#### 5. **Iterative Loop:**

```
var result, i uint
for i = 2; i < position; i++ {
result = slc[i-1] + slc[i-2]
slc[i] = result
}
```

The loop starts from

`i = 2`

and runs until it reaches the`position`

.In each iteration, the Fibonacci number at index

`i`

is calculated as the sum of the two previous Fibonacci numbers (`slc[i-1]`

and`slc[i-2]`

).The result is stored both in

`result`

and in the slice`slc[i]`

for future calculations.

#### 6. **Returning the Result:**

```
return result
```

- Once the loop finishes, the variable
`result`

holds the Fibonacci number at the desired position, and the function returns it.

This is a more *efficient* approach to calculating Fibonacci numbers compared to recursion, especially when `position`

is large, because **it doesnâ€™t repeat unnecessary calculations** and we are proving by using benchmark tests** .** Letâ€™s prove it.

## How to Run the Benchmark Tests

Now, for the benchmark tests, letâ€™s write some test. First, you will need to create a **maintest.go** file. In it, using Golang's documentation on benchmark tests, you can create the functions to be tested as follows:

```
// main_test.go
// Benchmark for Iterative Function
func BenchmarkFibIterative(b *testing.B) {
for i := 0; i < b.N; i++ {
fibIterative(uint(10))
}
}
// Benchmark for Recursive Function
func BenchmarkFibRecursive(b *testing.B) {
for i := 0; i < b.N; i++ {
fibRecursive(uint(10))
}
}
```

Let's run the test for position 10 and then increase appropriately. To run the benchmark tests, you simply run the command `go test -bench=NameoftheFunction`

.

If you want to know more about this command, check here. Letâ€™s check the function for **position 10**:

```
func BenchmarkFibIterative(b *testing.B) {
for i := 0; i < b.N; i++ {
fibIterative(uint(10))
}
}
```

```
go test -bench=BenchmarkFibIterative
Results:
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkFibIterative-8 27715262 42.86 ns/op
PASS
ok playground 2.617s
```

Letâ€™s analyze with the help of this image:

According to the image, we have 8 cores for the tests, and no time limit (it will run until completion). It took **27_715_262 iterations** and **1.651 seconds** to complete the task.

```
func BenchmarkFibRecursive(b *testing.B) {
for i := 0; i < b.N; i++ {
fibRecursive(uint(10))
}
}
```

```
go test -bench=BenchmarkFibRecursive
Results:
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkFibRecursive-8 6644950 174.3 ns/op
PASS
ok playground 1.819s
```

Using the same image to analyze the result, in this case it took **6_644_950 iterations** and **1.819 seconds** to complete the task we have:

Fibonacciâ€™s Function | Position | Iterations | Time to run (s) |

Iterative | 10 | 27_715_262 | 1.651 |

Recursive | 10 | 6_644_950 | 1.819 |

The **benchmark results** show that the iterative approach is significantly more efficient than the recursive approach for calculating the Fibonacci sequence.

For position 10, the iterative function ran approximately **27.7 million iterations** in **1.651 seconds**, while the recursive function managed only **6.6 million iterations** in **1.819 seconds**. The iterative method outperformed the recursive method both in terms of iterations and time, highlighting its efficiency.

To proven even further this, letâ€™s try with the **position 40** (4 times the previous value):

```
// Results for the Iterative Function
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkFibIterative-8 9904401 114.5 ns/op
PASS
ok playground 1.741s
// Results for the Recursive Function
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkFibRecursive-8 4 324133575 ns/op
PASS
ok playground 3.782s
```

Fibonacciâ€™s Function | Position | Iterations | Time to run (s) |

Iterative | 40 | 9_904_401 | 1.741 |

Recursive | 40 | 4 | 3.782 |

The benchmark results clearly highlight the efficiency difference between the iterative and recursive approaches for calculating Fibonacci again.

The **iterative function** completed approximately **9.9 million iterations** with an average execution time of **114.5 nanoseconds per operation**, finishing the benchmark in **1.741 seconds**. In stark contrast, the **recursive function** only completed **4 iterations** with an average execution time of **324,133,575 nanoseconds per operation** (over 324 milliseconds per call), taking **3.782 seconds** to finish.

These results demonstrate that the recursive approach is far less efficient due to repeated function calls and recalculations, making the iterative method vastly superior in both speed and resource usage, especially as input size increases.

Just out of curiosity, I tried **position 60** and it literally crashed the test:

```
// Results for the Iterative Function
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkFibIterative-8 7100899 160.9 ns/op
// Results for the Recursive Function
SIGQUIT: quit
PC=0x7ff81935f08e m=0 sigcode=0
goroutine 0 gp=0x3bf1800 m=0 mp=0x3bf26a0 [idle]:
runtime.pthread_cond_wait(0x3bf2be0, 0x3bf2ba0)
...
```

## Conclusion

If your production code is running slowly or is unpredictably slower, you can use this technique, combined with **pprof** or other tools from the built-in testing package, to identify and test where your code is performing poorly and work on how to optimize it.

Remember: Code that is beautiful to the eyes is not necessarily more performant.

### Reference

### Homework

This article explains why for some small numbers, the recursive strategy is better. Can you find a better way to improve the recursive function? (Tip: use Dynamic Programming).