Explain the core tradeoff inlining makes between heap allocation/call overhead and bytecode size, and how you'd decide for a given function.
answer
- Space (bytecode) for time+allocation trade
- Bloat ~ body_size x call_sites
- Win: small body + lambda + hot path
- No lambda -> no benefit
- Public -> add ABI freeze to the calculus
basics
~20 sInlining trades one thing for another: it removes the lambda object and extra call, but copies the function's code into every call site, making the program bigger. Inline when the body is small and called a lot; skip it when the body is big.
solid answer
~50 sInlining is a space-for-time-and-allocation trade. **Gain**: each call site no longer allocates a `FunctionN` lambda object or pays the `invoke()` dispatch — valuable in hot loops and allocation-sensitive code. **Cost**: the function body plus its lambda bodies are duplicated at every call site, so total bytecode grows roughly with `body_size × call_sites`. The decision balances those: a small body (say a few statements) with a lambda parameter, called frequently, is a clear win — the saved allocations dwarf the tiny duplication. A large body multiplies code with no proportional benefit, and a body with no lambda parameter saves nothing (the compiler warns). I'd also weigh whether it's library-public (bloat ships to consumers, freezes the body) and whether the JIT would already devirtualize. In practice: inline tiny lambda-helpers, profile before inlining anything non-trivial, and prefer keeping large logic in shared non-inline methods.
go deeper
States the trade as 'no lambda object vs bigger code'.
Quantifies bloat as body x call sites and gives a decision checklist including hotness and lambda presence.
Adds JIT devirtualization, 64KB limit, and library/ABI considerations to the decision.
Frames it as moving cost from runtime to space and ties the decision to measured workloads and API policy.
## The two sides of the trade ### What you GAIN by inlining - **No lambda allocation**: without inline, each lambda passed to a higher-order function is (often) a heap `FunctionN` object. Inlining removes it. - **No call indirection**: the lambda's `invoke()` virtual dispatch disappears; the code is straight-line. These matter most in **hot paths** and **allocation-sensitive** code (tight loops, GC-pressure-prone services). ### What you PAY - **Bytecode duplication**: the body and the lambda bodies are emitted at *every* call site. Total size scales roughly with `body_size × number_of_call_sites`. - Knock-on effects: larger artifacts, worse instruction-cache locality, risk of the 64KB method limit, and (for libraries) the body freezing into consumers. ## A decision checklist Ask, in order: 1. **Does it even take a lambda?** No → don't inline (no allocation to save; compiler warns). 2. **Is the body small?** Large bodies make duplication expensive; keep big logic in a shared non-inline method. 3. **Is it hot / allocation-sensitive?** If the function is rarely called, the saved allocation is negligible and the JIT may handle it anyway. 4. **Is it library-public?** If yes, factor in ABI freezing and `@PublishedApi` exposure; lean toward a thin inline shim or no inline. 5. **Did you measure?** For anything non-trivial, benchmark — assumptions about lambda cost are often wrong once the JIT devirtualizes. ```kotlin // Clear win: small body, lambda param, called in a loop inline fun <T> Iterable<T>.eachIndexedFast(action: (Int, T) -> Unit) { var i = 0 for (e in this) action(i++, e) } // Poor fit: big body, lambda or not -> keep as a normal function fun renderReport(data: Report): String { /* 60 lines */ } ``` ## Mental model Think of inline as moving cost from **runtime (allocations/dispatch)** to **build-time/space (bytecode)**. You want that trade only when the runtime cost is real and the space cost is tiny — i.e., small, hot, lambda-taking helpers.
- Roughly how does total inlined bytecode scale?About body_size times the number of call sites, since the body is duplicated at each site.
- If a hot function takes a lambda but has a large body, what's a good middle ground?Keep the function non-inline (or a thin inline wrapper) and move the bulk into a shared non-inline method, so you avoid duplicating the large body while still optionally inlining the small lambda-dispatch part.
Inlining is buying in bulk: cheaper per use (no per-call overhead) but it takes up shelf space everywhere you store a copy.
saying these in an interview costs you the question
- Treating inline as free performance
- Ignoring call-site count when judging bloat
- Inlining cold-path functions for 'speed'
- Forgetting the JIT may already remove the overhead
- Not measuring before inlining large bodies