← Concepts

阅读中文版本

Hot Path Inlining

Hot path inlining copies the body of a frequently called small function into the caller, eliminating the call overhead and enabling further optimization across the inlined boundary.

performance

Definition

Hot path inlining is the optimization — by the compiler or by hand — of replacing a function call with the function body at the call site, so that the call overhead (stack frame, branch) is eliminated and the optimizer can see across the boundary, applied selectively to the functions on the critical path.

Context and scope

Applied in JIT compilers, performance-sensitive libraries, and any code where a small function is called in a tight loop and the call overhead is measurable.

Common misconceptions

  • Inlining is always faster — it is faster in isolation; it increases code size, which can hurt instruction cache locality and make the whole program slower
  • The compiler inlines optimally — it inlines within its budget and heuristics; very hot cross-module calls may need hinting or manual inlining

Anti-patterns

  • Forcing inlining of a large function, bloating the code and evicting other hot code from the instruction cache
  • Inlining a function that is called from many sites, so a change to the body requires recompiling every caller

Related concepts

Authoritative source

https://llvm.org/docs/Passes.html#inline-functions

What it is

Hot path inlining is the optimization — by the compiler or by hand — of replacing a function call with the function body at the call site, so that the call overhead (stack frame, branch) is eliminated and the optimizer can see across the boundary, applied selectively to the functions on the critical path. Applied in JIT compilers, performance-sensitive libraries, and any code where a small function is called in a tight loop and the call overhead is measurable. Performance is a distribution, not a number: the tail determines user experience, and the mean hides the outliers that cause abandonment.

How it works

Measurement targets percentiles at the workload’s actual concurrency, not a single sequential run. The fast path is kept fast by deferring work that is not on it — lazy initialization, coalescing writes, avoiding allocation in hot loops. Profiling finds the bottleneck; micro-optimization without a profile optimizes the wrong thing. Constant factors matter, but algorithmic complexity dominates as scale grows.

When to optimize and pitfalls

Optimize when a measurement shows a concrete cost — latency budget burn, compute spend, user-visible jank. The pitfall is optimizing for the benchmark rather than the workload: a synthetic test that does not reproduce the production access pattern produces a local maximum that hurts globally. Another is trading memory for speed without accounting for the GC or eviction cost the extra allocation introduces.