Execution

JIT Compilation

Compiling with information a static compiler cannot have. Tiers, profiling, speculation, guards, and the deoptimization that catches a wrong guess.

Just-in-Time Compilation
▶ lab

Start the program immediately by interpreting it, watch which code actually runs, and compile that code to native instructions while the program is still running — using facts about this execution that no ahead-of-time compiler could have had.

Q · What does a JIT actually do, and at what moment does it do it?
Why Runtime Information Helps
▶ lab

A static compiler must be correct for every type that could occur, every branch that could be taken and every target a call could reach. A JIT sees which ones actually occur, and specializing to the actual case is worth far more than any amount of extra analysis on the general one.

Q · What can a compiler know at run time that it genuinely cannot know at build time, and why is that worth so much?
Tiered Compilation
▶ lab

Not one compiler but several, arranged from instant-and-slow to expensive-and-fast, with code promoted upward as it proves hot and demoted back down when a speculation fails. Startup and steady state stop competing for the same knob.

Q · Why do engines run several compilers instead of one good one, and what decides which tier a piece of code is in?
Profiling and Hotness
▶ lab

Deciding what to compile is a measurement problem with a cost on both sides: compile too eagerly and you spend time on code that never repays it, compile too late and the program runs slowly through the window where it mattered most.

Q · How does a runtime decide that a piece of code is worth compiling, and what does getting the threshold wrong actually cost?
On-Stack Replacement
▶ lab

A function that was entered once and has been looping for a minute cannot benefit from being compiled, because nothing will call it again. On-stack replacement swaps the running activation itself over to optimized code mid-loop, which means translating a live frame from one code version's layout into another's.

Q · A function has been running one loop for thirty seconds. Compiling it will not help, because it will never be called again — so how does it ever get faster?
Speculative Optimization
▶ lab

"This value has been a small integer every time, so compile an integer fast path." The profile is evidence, not proof — which is exactly why the fast path is preceded by a check, and why the whole apparatus of guards and deoptimization exists behind it.

Q · How can a compiler emit code that assumes something it has not proved, and still be correct?
Guards
▶ lab

The cheap runtime check that turns an assumption into a sound one. A guard is a comparison, a branch and a piece of metadata — and its cost is the bar every speculation has to clear before it is worth making.

Q · What does the check in front of a speculative fast path actually cost, and what does it have to do besides compare two values?
Deoptimization
▶ lab

A guard fails, and execution must continue correctly in code that assumed nothing — which means reconstructing an interpreter frame from an optimized one. Keeping that reconstruction possible is a standing obligation, and the obligation, not the mechanism, is what this lesson is about.

Q · When an optimized function's assumption turns out to be wrong, how does the program carry on correctly — and what did the compiler have to give up to make that possible?
Inline Caches
▶ lab

Cache the resolved answer at the call site itself, guarded by a check of the key that produced it. One target is monomorphic and nearly free; a few is polymorphic and still cheap; many is megamorphic, and the right response is to stop caching rather than to cache harder.

Q · A dynamic call has to look up its target every time. How does caching that lookup at the call site work, and why does it stop working when the site sees too many things?
What a JIT Costs
▶ lab

Compilation on the user's critical path, a warmup period where the program is measurably slower than itself, memory for code and profiles, benchmark numbers that will not sit still, and a writable-then-executable memory region that some platforms refuse to allow at all.

Q · What am I actually paying for a JIT, and when is the bill larger than the benefit?