English static mirror for SEO/GEO · AI-assisted translation · Read Chinese original

Go Inlining Reality Check: No Force Pragma, Only Small Functions Plus -m Verification

Forum topic · 小凯 · 2026-08-17

Summary

Drawing on real compilation output from Go 1.26.5 using `go build -gcflags="-m -m"`, this article clarifies that Go has no `//go:inline` directive to force inlining, only `//go:noinline` to forbid it. The gc compiler inlines small, cheap functions automatically, with an empirical cost budget of around 80; exceeding it (e.g., 98 from `reflect`, 523 from a large body) blocks inlining. Each function is decided independently, so calling a non-inlineable function does not block the caller. Top-level `defer`, `recover` inside closures, and hot-path `reflect` are concrete rejection triggers. Interface methods can inline after devirtualization. Inlining is not the same as speed: it can bloat binaries and must be validated via `-m` flags, profiling, or hand-written assembly.

Key points

  • No force pragma exists. Go intentionally provides only //go:noinline to disable inlining; there is no //go:inline or C/C++-style __attribute__((always_inline)).
  • Inlining is automatic and per-function. The gc compiler uses an SSA inlining cost budget (empirically ~80). Functions whose body cost exceeds the budget are rejected; each function is decided independently, so calling a non-inlinable function does NOT block the caller from being inlined.
  • Concrete rejection triggers verified on Go 1.26.5: top-level defer (unhandled op DEFER), recover inside a closure (call to recover), and reflect calls that blow past the cost budget.
  • Interface methods can inline. When the call site knows the concrete type, devirtualization allows Cat.Sing-style interface methods to be inlined.
  • Small loops and switches are fine; large loops/deep branches exceed the budget.
  • Receiver type (value vs pointer) doesn't matter directly, but copying a large struct via a value receiver adds to the cost.
  • Practical recipe

    1. Keep the function body small enough to stay under the cost budget. 2. Remove top-level defer, recover, and reflect from hot paths. 3. Verify with:

  • go build -gcflags="-m" . — look for can inline <Func>
  • go build -gcflags="-m -m" . — see the rejection reason (cannot inline <Func>: <reason>)
  • go build -gcflags="-l" . — disable inlining globally for debugging (stack -l to disable more passes)
  • Note: in Go 1.26 the -m=2 output format changed, so single-level -m -m is the most stable option.

    Three myths debunked by testing

  • "Calling a non-inlineable function blocks you" — false. A wrapper that calls a //go:noinline function can still be inlined.
  • "Since Go 1.14 all top-level defers inline" — false on Go 1.26.5; top-level defer still produces unhandled op DEFER. Only the closure body inside defer is eligible, and even then it cannot contain recover.
  • "//go:inline can force inlining" — does not exist. The engineering reason: forced inlining slows compilation, bloats binaries, and breaks the compiler's de-inlining debugging capability.
  • Caveats

  • Inlining ≠ performance. It removes call overhead and opens doors for constant propagation and escape analysis, but it grows the binary and can push hot functions past the budget. Profile before optimizing.
  • //go:noinline is a debugging/profiling probe, not an optimization tool. For extreme hot paths, rewrite with optimized libraries (e.g., kelindar/simd) or hand-written assembly rather than forcing inlining.
  • The gc compiler does not auto-vectorize ordinary loops; SIMD only exists in selected standard-library functions.

Methodology

All claims are verified against Go 1.26.5 using go build -gcflags="-m -m" . against a two-file demo (main.go + blocked.go) covering both inlinable and rejected cases, so the outputs are reproducible.

One-line takeaway: write small functions → confirm can inline with -m → if rejected, shrink the body and remove top-level defer/recover/reflect → for ultimate speed, reach for hand-written assembly or an existing SIMD library.

Tags

#go#inlining#compiler-optimization#performance-tuning#gc-flags#ssa-backend#defer-recover#simd

This page is an English static mirror generated for search and AI citation. It may be a full translation or structured summary of the Chinese original. Canonical interactive discussion lives on the Chinese page: https://zhichai.net/topic/178633591