Inline Expansion
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). detection_hints.automated is 'no'; over-inlining causing code bloat and i-cache misses is silent until profiling or careful review of always_inline on large functions reveals it. No linter flags this.
Closest to 'simple parameterised fix' (e3). quick_fix is to write small focused functions and remove unwarranted inline hints, or manually-inlined bodies; a localised pattern replacement rather than a single-line swap, but contained.
Closest to 'localised tax' (b3). Manual inlining or misplaced always_inline hurts readability and maintainability in the affected component, but the choice doesn't shape the whole system; it stays local to the hot path / helper.
Closest to 'serious trap' (t7). The misconception is that `inline` guarantees inlining — it is only a hint the cost model routinely overrides in both directions, contradicting the keyword's apparent meaning. Multiple common_mistakes (recursion depth limits, virtual-call inlining) reinforce a strong, counterintuitive gotcha.
Also Known As
TL;DR
Explanation
In JIT runtimes such as the JVM HotSpot and V8, inlining decisions are made at runtime using profiling data: hot call sites are inlined and may be deoptimized if assumptions break. PHP 8's JIT performs more limited inlining and is far less aggressive than these mature runtimes.
Common Misconception
Why It Matters
Common Mistakes
- Assuming the `inline` keyword forces inlining when it is merely a hint the optimizer may ignore.
- Manually inlining function bodies by hand, hurting readability for a gain the compiler already provides automatically.
- Expecting virtual or dynamically dispatched calls to be inlined without devirtualization or profile-guided information.
- Over-applying always_inline to large functions, causing code bloat and instruction cache misses that slow execution.
- Believing recursive functions can be fully inlined when compilers only inline a bounded depth.
Avoid When
- The function body is large, since inlining copies it to every call site and causes code bloat.
- Manually inlining by hand for readability-costly micro-optimizations the compiler already performs.
- The call target is virtual or dynamic and cannot be resolved without devirtualization.
When To Use
- Small, frequently called helpers like getters and accessors where call overhead dominates.
- Hot loops where inlining exposes constant folding, vectorisation, or cross-boundary register allocation.
- JIT runtimes inlining profiled hot call sites where runtime feedback confirms the benefit.
Code Examples
// C: forcing inlining of a large function hurts more than it helps
__attribute__((always_inline))
static inline int process(const int *data, int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += data[i] * data[i]; // large body
if (data[i] < 0) sum -= 1;
// ... many more lines ...
}
return sum;
}
// Called from 40 sites: body copied 40 times -> code bloat,
// instruction cache thrash, slower overall despite no call overhead.
// C: small hot helper, let the compiler decide
static inline int square(int x) {
return x * x; // tiny: a natural inlining candidate
}
int process(const int *data, int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += square(data[i]); // compiler inlines square here,
} // then hoists/vectorises the loop
return sum;
}
// Large functions stay as real calls; the optimizer's cost model
// inlines only where the body is small and the benefit is real.