JIT: why V8 has several compilers
Why V8 uses an interpreter and several compilers.
In the previous lesson, we saw how Ignition executes bytecode: it reads an instruction, selects its handler, and moves on. To reduce the cost of these operations, V8 can generate a machine-code version of the function. Running that version no longer requires selecting a handler for each bytecode instruction.
If a function is called once, lengthy optimization may not pay off. For a function that repeatedly performs substantial work, faster subsequent calls may offset the compilation cost. V8 therefore has several compilers with different roles.
Ignition is an interpreter. Sparkplug, Maglev, and TurboFan are compilers within the same V8 engine. We will compare their roles:
- InterpreterIgnition
- Executes bytecode through handlers. It does not need a separate machine-code version of the function.
- Allows execution to start without JIT-compiling the function.
- Fast compilerSparkplug
- Quickly translates bytecode into machine code without extensive optimization.
- Reduces interpretation overhead with a small compilation cost.
- Optimizing compilerMaglev
- Uses observations of the function, such as value types, to generate faster machine code.
- Invests more in optimization than Sparkplug, while being designed to compile faster than TurboFan.
- Optimizing compilerTurboFan
- Applies more extensive optimizations to generate fast code for functions that account for a significant share of execution time.
- More expensive compilation can pay off when a function runs for a long time or is called repeatedly.
V8 makes the decision for each function separately. A function does not have to pass through every tier listed here. The choice depends on the engine version, settings, and observations of program execution.