Allocation Hot-Path Recipes
Allocation Hot-Path Recipes
Overview
High allocation rate drives GC and cache pressure. After profiles point to a site, apply small, proven recipes—not premature sync.Pool everywhere.
Measure first
go test -bench=BenchmarkX -benchmem
# or pprof heap under loadRecipes
1. Avoid fmt in hot loops
// slow: fmt.Sprintf("%d", n)
strconv.Itoa(n)
strconv.AppendInt(buf[:0], int64(n), 10)2. Grow slices with known cap
out := make([]T, 0, len(in))3. Reuse buffers
var buf bytes.Buffer
buf.Reset()
// or sync.Pool of []byte for short-lived buffers4. strings.Builder
var b strings.Builder
b.Grow(n)
b.WriteString(...)
s := b.String()5. Interface boxing
Storing concrete values in any/interfaces allocates. Keep hot maps typed.
6. JSON
json.Encoderto writer vs repeatedMarshal
- Consider specialized codecs only when bench proves need
- Avoid
map[string]anyfor hot types
7. io.Copy buffer
buf := make([]byte, 32*1024)
io.CopyBuffer(dst, src, buf)Pool discipline
var pool = sync.Pool{New: func() any { return make([]byte, 0, 1024) }}
b := pool.Get().([]byte)
b = b[:0]
// use b
pool.Put(b)Don’t put pointers to data still in use; don’t assume zeroed memory.
Rules of thumb
| Do | Don’t |
|---|---|
| Fix algorithm first | Pool everything |
| Bench before/after | Optimize cold paths |
| Keep code readable | Unreadable zero-alloc for 0.1% |
Try next
-benchmema JSON encode path; reduce allocs/op.
- Replace
fmt.Sprintfin a hot path.
- Profile heap; fix top1 only.