How does inlining work in the Go compiler, how do you inspect it, and what prevents a function from being inlined?
Question 388HardGo 1.22 to 1.25
The compiler inlines functions whose body cost is under a budget (~80 AST nodes). Inlining removes call overhead, but the bigger win is that it enables further optimization at the call site: escape analysis can keep values on the stack, constants propagate, bounds checks disappear. Since Go 1.12 mid-stack inlining allows inlining non-leaf functions.
go build -gcflags='-m' ./... # "can inline X", "inlining call to X"
go build -gcflags='-m=2' ./... # includes reasons: "cost 95 exceeds budget 80"
Things that block or hinder inlining:
- Body too large (cost > 80); each call inside the body costs ~57 extra nodes.
- Statements the inliner doesn't handle:
recover,defer,go,select("unhandled op" in-m=2); the//go:noinlinedirective;//go:norace(only when building with-race); cgo wrapper functions. - Calls through interfaces or function values (dynamic dispatch) — unless devirtualized, e.g. via PGO.
- Recursive functions.
A classic trick is fast-path/slow-path splitting: keep the common case tiny so it inlines, and move the rare case to a separate non-inlined function.
func (b *Buf) WriteByte(c byte) error {
if len(b.data) < cap(b.data) { // fast path: inlinable
b.data = append(b.data, c)
return nil
}
return b.writeByteSlow(c)
}
func (b *Buf) writeByteSlow(c byte) error {
b.grow(1)
b.data = append(b.data, c)
return nil
}
Gotcha: the exact budget and rules change between releases — measure, don't guess.
More on Performance, Profiling & Testing
- Q386How does test coverage work in Go? Explain covermode, -coverpkg, and coverage for integration tests.
- Q387What is Profile-Guided Optimization (PGO) in Go, and how do you use it?
- Q389Explain escape analysis. Which of these allocate on the heap?
- Q390What is bounds check elimination (BCE), how do you see it, and how can you help the compiler?
- Q391What are the tradeoffs of sync.Pool? When does it help, and when does it hurt?
- Q392Compare the performance of string concatenation approaches: +, fmt.Sprintf, strings.Builder, bytes.Buffer, strings.Join.