Boosting Go Performance with Better Memory Layout ๐
When we talk about optimizing Go programs, the first things that come to mind are goroutines, channels, or clever algorithms. But thereโs a silent factor that often makes a huge difference: how your data is laid out in memory.
This is known as spatial locality โ and leveraging it can make your Go apps much faster without changing your logic.
Why Memory Layout Matters?
CPUs fetch memory in cache lines (small blocks, usually 64 bytes). If your data sits contiguously in memory, the CPU can grab an entire block at once. But if your data is scattered (like a linked list jumping around pointers), the CPU wastes cycles fetching from different places.
- Contiguous data โ Cache-friendly โ Fast ๐
- Scattered data โ Cache misses โ Slow ๐ข
Example: Linked List vs Slice
Letโs compare two ways of storing integers: a linked list and a slice.
package main
import (
"fmt"
"time"
)
// Linked list node
type Node struct {
value int
next *Node
}
// Build a linked list with n elements
func buildLinkedList(n int) *Node {
head := &Node{value: 0}
current := head
for i := 1; i < n; i++ {
current.next = &Node{value: i}
current = current.next
}
return head
}
// Traverse linked list and sum values
func sumLinkedList(head *Node) int {
sum := 0
for head != nil {
sum += head.value
head = head.next
}
return sum
}
// Traverse slice and sum values
func sumSlice(values []int) int {
sum := 0
for _, v := range values {
sum += v
}
return sum
}
func main() {
const N = 10_000_000
// Linked list
list := buildLinkedList(N)
start := time.Now()
sum1 := sumLinkedList(list)
fmt.Printf("Linked list sum=%d, time=%v\n", sum1, time.Since(start))
// Slice
values := make([]int, N)
for i := 0; i < N; i++ {
values[i] = i
}
start = time.Now()
sum2 := sumSlice(values)
fmt.Printf("Slice sum=%d, time=%v\n", sum2, time.Since(start))
}Expected Output (approx)
Linked list sum=49999995000000, time=220ms
Slice sum=49999995000000, time=15ms๐ On the same dataset, iterating over a slice can be 10โ20x faster than a linked list, purely because of memory layout!
Tips for Go Developers
- โ Prefer slices/arrays over linked lists/maps for iteration.
- โ Group โhotโ fields together in structs to reduce padding.
- โ Use batch allocations instead of spreading many small pointers across memory.
