Go Pointers with Structs โ Optimize¶
Instructions¶
Identify the issue, fix, explain. Difficulty: ๐ข ๐ก ๐ด.
Exercise 1 ๐ข โ Constructor Allocates Per Call¶
Problem:
func NewParser() *Parser { return &Parser{} } // 2 KB alloc per call
// Hot: 50k calls/sec โ 100 MB/sec alloc
Fix โ sync.Pool:
var parserPool = sync.Pool{New: func() any { return new(Parser) }}
func acquire() *Parser { return parserPool.Get().(*Parser) }
func release(p *Parser) { p.Reset(); parserPool.Put(p) }
Reduces allocation rate ~95% in steady state.
Exercise 2 ๐ข โ Pointer for Large Struct, Value for Small¶
Problem:
type Coord struct{ X, Y, Z float64 } // 24 B
func newCoord() *Coord { return &Coord{} } // unnecessary heap alloc
Fix โ return value:
For small types, value return is faster.
Exercise 3 ๐ก โ Method on Large Struct With Value Receiver¶
Problem:
type State struct{ Buffer [256]int }
func (s State) Sum() int { /* ... */ } // copies 2 KB per call!
Fix โ pointer receiver:
Cuts call cost from ~300 ns to ~150 ns + 0 alloc.
Exercise 4 ๐ก โ Reduce Pointer Density¶
Problem:
Fix โ value slice:
GC scan time drops dramatically. Cache locality improves.
When pointers are needed (sharing across structures): keep them. Otherwise prefer values.
Exercise 5 ๐ก โ Atomic Pointer for Snapshot Config¶
Problem:
var mu sync.RWMutex
var config *Config
func get() *Config { mu.RLock(); defer mu.RUnlock(); return config }
Fix โ atomic.Pointer:
Lock-free reads; faster under high concurrency.
Exercise 6 ๐ด โ Sub-Object Lifetime Pinning¶
Problem:
big := &Big{Sub: SubStruct{...}}
sub := &big.Sub
big = nil
// sub keeps Big alive (Sub is part of Big's allocation)
Fix โ copy out:
Exercise 7 ๐ด โ Struct Field Layout for Cache Locality¶
Problem:
type Bad struct {
A int8 // 1 byte
B int64 // 8 bytes โ but offset 8 due to padding
C int8 // offset 16
D int64 // offset 24 due to padding
}
// Total: 32 bytes (lots of padding)
Fix โ reorder by size descending:
type Good struct {
B int64 // offset 0
D int64 // offset 8
A int8 // offset 16
C int8 // offset 17
// 6 bytes padding to multiple of 8
}
// Total: 24 bytes
Verify with unsafe.Sizeof.
For high-volume data structures, the saved bytes ร millions of instances matters.
Exercise 8 ๐ด โ Verify Constructor Doesn't Heap-Allocate¶
Problem:
Verify stack allocation:
If you see "moved to heap", the pointer escaped. Investigate.
For non-escaping uses, &T{} stays on the stack โ no allocation cost.
Lesson: Profile escape behavior; don't speculate.
In this topic