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)
}
}