Allocation Hot-Path Recipes

Updated

September 8, 2026

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 load

Recipes

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 buffers

4. 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.Encoder to writer vs repeated Marshal
  • Consider specialized codecs only when bench proves need
  • Avoid map[string]any for 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

  1. -benchmem a JSON encode path; reduce allocs/op.
  2. Replace fmt.Sprintf in a hot path.
  3. Profile heap; fix top1 only.