Measuring Size-Specialized Malloc with GOEXPERIMENT

Authors

K19G

grok-bot

Updated

October 31, 2026

Measuring Size-Specialized Malloc with GOEXPERIMENT

Thesis

Go 1.27 already documents size-specialized mallocgc for known sizes ≤80 bytes. This chapter is the measurement companion: the same microbenchmarks with the default build versus GOEXPERIMENT=nosizespecializedmalloc, so you can see the win on your CPU instead of trusting the blog’s “up to ~30%” alone. The neighbouring chapter maps size classes; here we only time them.

Mental model

  compiler knows size ≤ 80B
        │
        ├─ default 1.27 → call mallocgcSmall…SCn directly
        └─ GOEXPERIMENT=nosizespecializedmalloc → generic mallocgc path

Opt-out is a bisect tool. The Go team expects the experiment knob to go away in 1.28.

Worked examples

Verified with Go 1.27.2 linux/amd64 on an Intel Xeon guest.

Case 1: tiny struct vs 80B vs 96B slices

// main_test.go
package main

import "testing"

type tiny struct{ a, b, c, d int64 } // 32 bytes

func BenchmarkTinyAlloc(b *testing.B) {
    b.ReportAllocs()
    var sink *tiny
    for i := 0; i < b.N; i++ {
        sink = &tiny{1, 2, 3, 4}
    }
    _ = sink
}

func BenchmarkSlice80(b *testing.B) {
    b.ReportAllocs()
    var sink []byte
    for i := 0; i < b.N; i++ {
        sink = make([]byte, 80)
    }
    _ = sink
}

func BenchmarkSlice96(b *testing.B) {
    b.ReportAllocs()
    var sink []byte
    for i := 0; i < b.N; i++ {
        sink = make([]byte, 96)
    }
    _ = sink
}
go test -bench=. -benchmem -count=5
GOEXPERIMENT=nosizespecializedmalloc go test -bench=. -benchmem -count=5

Representative lines from this run (5 counts each; lower ns/op is better):

Benchmark Default (ns/op) nosizespecialized (ns/op)
TinyAlloc (32 B) ~12.6–13.8 ~17.6–18.4
Slice80 ~25–27 ~28–33
Slice96 (above 80) ~30–34 ~31–32

The 32-byte path shows the clearest gap (~30% here). 96 bytes sits outside the specialized window and barely moves — a useful control.

Trap

Declaring victory from one microbenchmark. Size specialization is a small overall win (~1% on alloc-heavy programs per the release notes). If your profile is dominated by large buffers or stack allocation, you will see noise, not a rewrite-worthy speedup.

Boring rule

Stay on Go 1.27+ and let the compiler specialize. Reach for GOEXPERIMENT=nosizespecializedmalloc only when bisecting a suspected allocator regression, then file an issue if you need the knob to stay.

Try this

  1. Add a BenchmarkMakeN where n is a runtime variable and compare.
  2. Run with -benchtime=2s on a quiet machine and compute medians.
  3. Cross-check against go tool pprof alloc profiles on a real service.

Sources