Make you Go code work 1.5x faster OR even more
Tags: performance, go, opensource, programming
Published: 2022-10-12 04:55:31

## Introduction

Performance is **a key thing of everything**, we (humans) don't like to wait and waste our time. Therefore, sometimes quick solution **as most of the managers** suppose are better than slow but with good engineering and design. But **_today we are not speaking about management_**, but about code performance. We have a [small text formatting library](https://github.com/Wissance/stringFormatter) that allows us to format text using a template in convenient on our view way. These [not only formatting function do what `fmt.Sprintf` does in more convenient way but also provide an additional features](https://dev.to/evillord666/string-formatting-in-golang-3l7h). **Previous versions** of our module were loose in performance to `fmt.Sprintf`, but since **_1.0.1_** **we are better**. And today we are going to tell **_how to make any golang code work faster_**.

## Parameters and return values

There are two options to pass either arguments and/or get function result - by pointer and, by value, consider the following example:
```go
func getItemAsStr(item *interface{}) string   // 1st variant
func getItemAsStr(item interface{})  string   // 2nd variant
func getItemAsStr(item *interface{}) *string  // 3rd variant
func getItemAsStr(item interface{})  *string  // 4th variant
```
According to our performance tests, we could conclude the following:
1. We've got small performance rise when we pass arguments by pointer because we've got rid of arguments copying.
2. We've got performance decrease when we return a pointer to function local variable

Therefore, the most optimal variant is the 1st variant.

## Strings

Strings are immutable like in many other programming languages, therefore string concatenation using + operator is a bad idea, i.e. code like this is a very slow:
```go
var result string = ""
for _, arg := range args {
    result += getItemAsStr(&arg)
}
```
Every "+" creates a new string object as a result memory usage rise, performance significantly decreases due to spending sufficient time on allocations on new variables.

There is a better solution for string concat - use strings.Builder, but for a better performance you should initially enlarge buffer (using `Grow` function) to prevent it from some / all re-allocs, but if buffer size will be very large there will be a penalty to performance due to initial memory allocation will be slow, therefore you should choose initial buffer size wisely, i.e.:

```go
var formattedStr = &strings.Builder{}
formattedStr.Grow(templateLen + 22*len(args))
```

## Cycles

There are 2 ways to iterate over collection:
* using `range` expression
  ```go
  for _, arg := range args {
      // do some staff here
  }
  ```
* using traditional `for` with 3 expressions:
  ```go
  for i := start; i < templateLen; i++ {
      // do some staff here
  }
  ```
2nd variant is better by performance. But there are additional advantages of usage `for` with 3 expressions:
* reduce number of iterations, more iteration code is slower, 
  therefore, if you could affect your loop value, do it. This 
  can't be achieved with `range`;
* set initial value for iteration much higher if it is possible, 
  i.e. in our case:
  ```go
  start := strings.Index(template, "{")
  if start < 0 {
     return template
  }

  formattedStr.WriteString(template[:start])
  for i := start; i < templateLen; i++ {
      // iterate over i
  }
  ```

## Conclusion

Using such simple techniques we made our code run 1.5 times faster than it was, and now it works faster even than fmt.Sprintf, see our performance measurements:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3j7nytafa4htelyr8i9.png)
. We also suggest you to use [our library](https://github.com/Wissance/stringFormatter) instead of `fmt.Sprintf`, because if you prepare strings for sql queries or something like this, it is important to make them as faster, as it possible. We would be thankful if you give us a star on Github and start to follow our organization here and on Github too.