Boost Go Performance: Memory and Concurrency Optimization Techniques
This article presents practical Go performance tips, covering memory pooling, struct merging, pre‑allocating slices and maps, reducing temporary objects, managing goroutine stacks, using goroutine pools, avoiding blocking calls, minimizing CGO usage, and efficient string handling.
1 Memory Optimization
1.1 Merge small objects into a struct for a single allocation
Go runtime uses a memory pool with spans of 4 KB and a cache that stores lists of fixed‑size blocks. Merging several small objects into a single struct allows one allocation instead of many, reducing fragmentation and allocation overhead.
<code>for k, v := range m {
k, v := k, v // copy for capturing by the goroutine
go func() {
// using k & v
}()
}
</code> <code>for k, v := range m {
x := struct{k, v string}{k, v} // copy for capturing by the goroutine
go func() {
// using x.k & x.v
}()
}
</code>1.2 Allocate sufficient space for byte buffers and reuse them
When encoding/decoding protocols, use
bytes.Bufferor similar byte‑buffer objects and pre‑allocate a large enough capacity to avoid repeated allocations; reuse the buffers whenever possible.
1.3 Pre‑estimate capacity when creating slices and maps with make
Slices grow automatically: if the new size exceeds twice the current size, capacity jumps to the new size; otherwise it grows by 2× while under 1 KB and by ¼ of the current size beyond that.
Maps double their capacity on each growth and keep both
bucketsand
oldbucketsduring incremental rehashing.
Recommendation: specify the expected size when calling
make.
<code>m := make(map[string]string, 100)
s := make([]string, 0, 100) // note: third argument is capacity
</code>1.4 Avoid long call stacks that allocate many temporary objects
Goroutine stacks start at 4 KB (2 KB in Go 1.7) and grow by doubling when needed; they shrink when usage falls below ¼ of the allocated size. Excessive stack growth can cause performance loss.
Keep call stacks shallow and split complex logic across multiple goroutines.
If a long stack is unavoidable, consider pooling goroutines to limit stack allocations.
1.5 Reduce creation of temporary objects
GC pauses increase with the number of short‑lived objects. Reduce temporary objects by using local variables and grouping them into larger structs or arrays.
Prefer local variables.
Combine several locals into a single struct/array to reduce scanning overhead.
2 Concurrency Optimization
2.1 Use a goroutine pool for high‑concurrency tasks
Creating a large number of goroutines for lightweight tasks can degrade scheduler performance and increase GC overhead.
2.2 Avoid high‑concurrency calls to synchronous system interfaces
Non‑blocking operations such as network I/O, locks, channels,
time.Sleep, and asynchronous syscalls do not block the Go runtime. Blocking operations include local I/O, synchronous syscalls, and CGo calls. Isolate blocking calls in dedicated goroutines.
2.3 Avoid sharing mutable objects with mutexes under high concurrency
When many goroutines contend for the same mutex, performance can hit a tipping point. Keep goroutines independent or partition work to limit contention.
3 Other Optimizations
3.1 Minimize or avoid CGO usage
Cross‑language calls incur large stack and context‑switch overhead; reduce the number of CGO calls when possible.
3.2 Reduce []byte ↔ string conversions
String is immutable in Go; converting between
[]byteand
stringcopies data. Prefer
[]bytefor manipulation when possible.
<code>func Prefix(b []byte) []byte {
return append([]byte("hello"), b...)
}
</code>3.3 Prefer bytes.Buffer for string concatenation
String concatenation creates new strings each time. Use
bytes.Bufferwith a pre‑allocated size for high‑performance concatenation;
fmt.Sprintfis convenient for non‑critical paths.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.