This section provides coding guidelines from the Golang coding perspective that are beneficial for improving performance.

Prefer strconv over fmt

When converting primitives to strings or from strings to primitives, strconv is faster than fmt.

Not recommended:

for i := 0; i < b.N; i++ {
  s := fmt.Sprint(rand.Int())
}

Recommended:

for i := 0; i < b.N; i++ {
  s := strconv.Itoa(rand.Int())
}

Avoid converting strings to bytes

Avoid repeatedly creating a byte slice from a fixed string. Instead, perform the conversion once and capture the result.

Not recommended:

for i := 0; i < b.N; i++ {
  w.Write([]byte("Hello world"))
}

Recommended:

data := []byte("Hello world")
for i := 0; i < b.N; i++ {
  w.Write(data)
}

Specify container capacity

Specify the container capacity if possible to pre-allocate memory for the container. This minimizes subsequent allocations (by copying and resizing the container) when adding elements.

Map capacity hint

In most cases, provide capacity information when initializing with make().

make(map[T1]T2, hint)

Providing a capacity hint to make() will attempt to resize the map at initialization, reducing memory reallocation when adding elements to the map.

Note that, unlike slices, a map capacity hint does not guarantee complete pre-allocation, but is used to estimate the number of hashmap buckets required. Therefore, allocation may still occur when adding elements to the map, even when specifying map capacity.

Not recommended:

m := make(map[string]os.FileInfo)

files, _ := os.ReadDir("./files")
for _, f := range files {
    m[f.Name()] = f
}

// m is created without a size hint; there may be more allocations at runtime.

Recommended:

files, _ := os.ReadDir("./files")

m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
    m[f.Name()] = f
}

// m is created with a size hint; there may be fewer allocations at runtime.

Specify slice capacity

In most cases, provide capacity information when initializing a slice with make(), especially when appending to the slice.

make([]T, length, capacity)

Unlike maps, a slice capacity is not just a hint: the compiler will allocate enough memory for the provided slice capacity in make(), meaning that subsequent append() operations will result in zero allocations (until the slice's length matches its capacity, after which any append may resize to accommodate additional elements).

Not recommended:

for n := 0; n < b.N; n++ {
  data := make([]int, 0)
  for k := 0; k < size; k++{
    data = append(data, k)
  }
}

Recommended:

for n := 0; n < b.N; n++ {
  data := make([]int, 0, size)
  for k := 0; k < size; k++{
    data = append(data, k)
  }
}