completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Tiered compilation

Learn how JavaScript engines start with interpreters, watch hot code, then tier up through baseline and optimizing JIT compilers.

By the end, you can
  • 01
    Explain when compiling pays backCompare an interpreter, an ahead-of-time compiler, and a JIT compiler: how fast each one starts, how fast its code runs, and when compiling is worth the time.
  • 02
    Name the main browser tiersList V8's tiers (Ignition, Sparkplug, Maglev, TurboFan) and the tier names in SpiderMonkey and JavaScriptCore.
  • 03
    Spot warm-up in real workExplain how counters move code up a tier, why benchmarks need a warm-up, and why real code should never be written for one tier.

Start simple, then specialize

A JavaScript engine is the program inside a browser or Node that runs your code. When a page loads, the engine does not know which functions will matter. A small helper might run once. A function that draws each frame of an animation might run thousands of times.

The engine can run a function in two broad ways:

  • Read it one step at a time. This starts at once, but runs slowly.
  • First turn it into machine code, the instructions the computer's chip runs directly. This takes time first, but then runs fast.

Turning code into machine code is called compiling. The time it takes is the compile cost. Tiered compilation is the engine's plan: start every function the cheap way, watch which ones run a lot, and spend compile time only on those.

Definition

Tiered compilation means the engine has several levels, called tiers, for running code. New code starts in a quick, cheap tier. The engine counts how often each function runs. A function that runs a lot is hot; one that rarely runs is cold. Hot functions are compiled again into faster tiers, which cost more to build.

This lesson covers:

  • interpreters, compilers, and JIT compilers;
  • the tier names in V8, SpiderMonkey, and JavaScriptCore;
  • the counters that decide when code moves up a tier.

The next lessons go deeper on type feedback and inline caches. Later lessons cover what the engine does when its guesses about your code turn out wrong. For the bigger picture, go back to Meet the engines, Bytecode & interpreters, and Startup performance.

Interpreter, compiler, and JIT: when each one pays

TRADE-OFF

There are three ways to run code. Each one pays its cost at a different time:

  • An interpreter runs bytecode, a simple list of steps the engine makes from your code. It runs one step at a time. It starts almost at once. But every step costs a little extra, because the interpreter must look up what each step means.
  • An ahead-of-time compiler turns the whole program into machine code before it runs. The result runs fast. But doing this for every function on a page would make the page slow to start.
  • A just-in-time (JIT) compiler compiles while the program is already running. It waits until a function has run enough to show that the compile cost will pay back.
Real-life analogyImproving a school timetable for busy classes

A new school starts with a rough timetable on day one. Over the next weeks, the staff improve the plan for the busiest classes first. The rough plan lets school start at once. The careful work goes where it helps the most students.

In real life: A rough timetable works on the first day
In JavaScript: An interpreter, such as V8's Ignition, starts new code quickly
In real life: A better timetable takes time to prepare
In JavaScript: Fast machine code costs compile time first
In real life: The busiest classes get the careful work
In JavaScript: Only hot functions get the expensive tiers
In real life: A quick fix helps before the final plan is ready
In JavaScript: A baseline compiler makes faster code without heavy work

The code below is a cost model. It uses made-up time units to show the idea. It is not real engine timing.

Compile cost plus the cost of each callPop out in the code editor (opens in a new tab)JavaScript
function totalTime(compileCost, callCost, calls) {  return compileCost + callCost * calls;} console.log(totalTime(0, 5, 4));      // interpreter: cheap startconsole.log(totalTime(120, 0.6, 4));  // top tier: too much compile costconsole.log(totalTime(120, 0.6, 400)); // top tier: compile cost pays back
  • Line 1 defines totalTime. It takes the compile cost, the cost of one call, and the number of calls.
  • Line 2 adds the compile cost to the cost of all the calls.
  • Line 5 is the interpreter: nothing to compile, 5 units per call, 4 calls. It prints 20.
  • Line 6 is the top tier with only 4 calls: 120 to compile, 0.6 per call. It prints 122.4. The compile cost is far bigger than the time the fast calls save.
  • Line 7 is the top tier with 400 calls. It prints 360. The interpreter would need 2000 units for 400 calls, so here compiling pays back.
The totalTime cost model. With 4 calls, the interpreter costs 20 and the top tier costs 122.4. With 400 calls, the top tier costs 360.4 callsinterpreter200 to compile5 per calltop tier122.4120 to compile0.6 per call400 callstop tier360120 to compile0.6 per call
For 4 calls, the interpreter costs less because it has nothing to compile. For 400 calls, the cheap calls of the top tier pay back its compile cost of 120.

Each row of the next table is one tier. Read across to see how long the tier takes to build, how fast its code runs, and what code it suits. Not every function climbs all the tiers; most stay low.

What each tier costs and what it is good for
TierHow long it takes to buildHow fast it runsWhat code it suits
InterpreterAlmost no time.Slowest for each step.Cold code, startup, and code that runs once.
Baseline JITA little time.Faster: simple machine code, with no lookup for each step.Functions that ran enough to be worth quick machine code.
Mid-tier optimizing JITSome time.Good code, made quickly.Hot code that is not yet worth the top tier's long compile.
Top-tier optimizing JITThe most time.Fastest, once it has seen how the code behaves.Very hot loops, and functions that keep running for a long time.

V8: Ignition, Sparkplug, Maglev, TurboFan

V8

V8 is the engine in Chrome and Node. It has four tiers, in this order:

  • Ignition is the interpreter. It runs bytecode.
  • Sparkplug is a baseline compiler. It turns bytecode straight into simple machine code, without trying to make it clever. V8 announced it in 2021. It is quick enough to help code that only runs for a short time.
  • Maglev is a mid-tier optimizing compiler. An optimizing compiler uses what it has seen the code do to make faster machine code. Maglev makes good-enough code quickly. It arrived in Chrome M117 in 2023.
  • TurboFan is the top tier. It spends the most compile time to make the fastest code.
V8's four tiers in order: Ignition, the interpreter. Sparkplug, the baseline compiler. Maglev, the mid tier. TurboFan, the top tier. Hotter code can move further right.IgnitioninterpreterSparkplugbaselineMaglevmid tierTurboFantop tierhotter code: more compile time, faster runs
A function starts in Ignition. If it keeps running, V8 can move it one step to the right. Each step spends more compile time to get faster code.

TurboFan's inside is changing too. Before a compiler makes machine code, it turns your code into a working form of its own. TurboFan used a form called "Sea of Nodes". V8's 2025 post "Land ahoy: leaving the Sea of Nodes" describes the move to a new back end called Turboshaft. Turboshaft uses a form that keeps the code's steps in order, called a control-flow graph.

Chrome and Node can differ

Chrome and Node can turn on different V8 tiers. The Node 22.23.1 build used for this lesson has Sparkplug and TurboFan turned on. Its --v8-options list shows Maglev as off by default (--no-maglev). Asking it to use Maglev prints "Maglev is not enabled." Chrome does use Maglev.

Which tier is this?
  • A function has just been turned into bytecode and runs for the first time in V8.
  • V8 turns bytecode into simple machine code, without heavy optimizing.
  • A hot function needs better code soon, but is not yet worth the top tier's long compile.
  • A very hot function is worth the longest compile, to get the fastest code.
  • JavaScriptCore starts code in a low-level interpreter, before its JIT tiers.
Try it yourself
0 of 5 correct

Put each card in the group it describes. The groups are general ideas; each engine picks its own exact numbers.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

SpiderMonkey and JavaScriptCore use the same idea

OTHER ENGINES

Other engines use the same plan with different names.

SpiderMonkey is Firefox's engine. It keeps a warm-up counter for each function. The counter goes up each time the function starts, and each time one of its loops goes round. SpiderMonkey's tiers are:

  • the C++ interpreter;
  • the Baseline Interpreter;
  • the Baseline Compiler;
  • Ion, the optimizing compiler.

In 2020, the Warp project changed how Ion gets its information. A part called WarpBuilder saves a copy of the bytecode and of what the code has seen so far. Then Ion's working form of the code, called MIR, is built on a background thread. A background thread is a second line of work, so the page does not have to wait for it.

JavaScriptCore is Safari's engine. Its tiers are LLInt (the interpreter), Baseline JIT, DFG, and FTL. WebKit's 2016 B3 post explains that FTL moved from the LLVM back end to its own B3 back end. WebKit's 2020 post on speculation explains how JavaScriptCore makes careful guesses about your code so it can run it fast.

The details differ, but the shape is the same: a cheap start, a quick baseline tier, then stronger optimizing tiers.

Each row of the next table is one engine. Read across from the tier where code starts to the top tier.

The tier names in each browser engine
EngineWhere code startsBaseline tierMiddle tierTop tier
V8IgnitionSparkplugMaglevTurboFan, whose back end is moving from Sea of Nodes to Turboshaft
SpiderMonkeyThe C++ interpreter, then the Baseline InterpreterBaseline CompilerNo separate middle tierIon, the optimizing compiler, which the Warp project feeds with what the code has seen
JavaScriptCoreLLIntBaseline JITDFG JITFTL JIT, which uses the B3 back end

Hotness counters: when code moves up a tier

COUNTERS

The engine needs a sign that code is hot. The simplest sign is a call counter: each call to a function adds 1. When the count reaches a set number, called a threshold, the engine moves the function to a stronger tier. This is called tiering up.

Real engines use more signals than one counter:

  • how many times a function was called;
  • how many times its loops went round;
  • feedback: notes about the kinds of values the code has seen;
  • other rules that each engine picks.

The optimizing compile can run on a background thread. Meanwhile, the main thread (the one running your page) keeps running the older, slower code until the new code is ready.

A small call-counter modelPop out in the code editor (opens in a new tab)JavaScript
const thresholds = { baseline: 6, mid: 30, top: 90 };let tier = "interpreter";let invocationCount = 0; function scoreCart(items) {  invocationCount += 1;  if (invocationCount === thresholds.baseline) tier = "baseline";  if (invocationCount === thresholds.mid) tier = "mid";  if (invocationCount === thresholds.top) tier = "top";  return { tier, total: items.reduce((sum, item) => sum + item.price, 0) };} for (let call = 1; call <= 8; call += 1) {  const report = scoreCart([{ price: call }, { price: 2 }]);  console.log(call, report.tier, report.total);}
  • Line 1 sets the thresholds: baseline at call 6, mid tier at call 30, top tier at call 90.
  • Line 2 starts the function in the "interpreter" tier.
  • Line 6 adds 1 to invocationCount on every call. An invocation is a call.
  • Lines 7 to 9 switch the tier when the count reaches a threshold.
  • Lines 14 and 15 call the function 8 times and print the call number, the tier, and the total.

Calls 1 to 5 print interpreter. Call 6 prints 6 baseline 8. The number 6 is only a choice made for this model. What matters is that the tier changes because of what the running code did.

Eight calls to scoreCart. Calls 1 to 5 run in the interpreter. At call 6 the counter reaches the baseline threshold of 6, so calls 6 to 8 run in baseline code.interpreter12345calls 1 to 5count is below 6baseline678calls 6 to 8count reached 6
Each call adds 1 to invocationCount. When the count reaches 6, the model sets tier to "baseline". The totals stay the same; only the tier changes.
How often code runs vs. what values it seesPop out in the code editor (opens in a new tab)JavaScript
const observedInputs = new Set(); function add(left, right) {  observedInputs.add(typeof left + "/" + typeof right);  const state = observedInputs.size === 1 ? "stable" : "mixed";  return String(left + right) + " " + state;} console.log(add(2, 3));console.log(add(4, 5));console.log(add("4", 5));

This model keeps a Set, a list with no repeats, of the kinds of values add has seen. typeof gives the kind of a value, such as "number" or "string".

  • Line 4 saves the pair of kinds, such as "number/number".
  • Line 5 says "stable" if only one pair was ever seen, and "mixed" if more than one was.
  • Lines 9 and 10 pass two numbers. They print 5 stable and 9 stable.
  • Line 11 passes the string "4" and the number 5. + joins them into "45". A new pair is seen, so it prints 45 mixed.

So the engine watches two different signals:

  • Running often (heat) tells it that compiling may pay back.
  • Seeing the same kinds of values (feedback) tells it that fast code made just for those kinds is likely to work.

Real engines keep much richer feedback than this Set.

Step through: calls make a function worth compiling
Step 0 of 21Ready
Your turn: follow the blue line

Step through the call-counter model: the counter goes up on each call, and the function moves from the interpreter to baseline code.

Running in
  1. script
Next: line 2
const thresholds = { baseline: 6, mid: 30, top: 90 };let invocationCount = 0; function scoreCart(items) {  invocationCount += 1;  if (invocationCount === thresholds.baseline) tier = "baseline";  if (invocationCount === thresholds.mid) tier = "mid";  if (invocationCount === thresholds.top) tier = "top";  return { tier, total: items.reduce((sum, item) => sum + item.price, 0) };} for (let call = 1; call <= 8; call += 1) {  const report = scoreCart([{ price: call }, { price: 2 }]);  console.log(call, report.tier, report.total);}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Counting calls is not enough. One call can run a long loop. So engines also watch how far loops get. In the next model, a number called interruptBudget goes down on every step of the loop. When it reaches 0, the engine knows the loop is hot.

A loop budget modelPop out in the code editor (opens in a new tab)JavaScript
let tier = "interpreter";let interruptBudget = 18; function crunchLoop(limit) {  let total = 0;  for (let i = 1; i <= limit; i += 1) {    interruptBudget -= 3;    if (interruptBudget <= 0 && tier === "interpreter") {      tier = "baseline"; // model collapses request, compile, install, and OSR entry      interruptBudget = 18;    }    total += i;  }  return { tier, total };} console.log(crunchLoop(8));
  • Lines 1 and 2 start in the interpreter with a budget of 18.
  • Line 7 takes 3 from the budget on each step of the loop.
  • Line 8 checks whether the budget has run out while the loop is still in the interpreter.
  • Line 9 switches the tier to "baseline". Line 10 sets the budget back to 18.
  • Line 17 calls crunchLoop(8) once. It prints { tier: 'baseline', total: 36 }.

The budget reaches 0 on step 6, so the switch happens in the middle of the loop. Moving a running loop into compiled code, without starting the function over, is called on-stack replacement, or OSR. This lesson shows only the idea. A later lesson shows how it works inside the engine.

The loop's interruptBudget drops by 3 on each step: 15, 12, 9, 6, 3, 0. When it reaches 0 at step 6, the tier switches to baseline and the budget goes back to 18.interruptBudget15129630steps 1 to 6: minus 3 eachtierbaselinebudget back to 18
One call, but a long loop. The budget drops by 3 on each step. At 0, the loop moves to baseline code without starting over: that is the idea of OSR.
The replay shows four steps as one

In a real engine, a used-up budget starts four steps:

  1. The engine asks for a stronger tier.
  2. The new code is compiled, maybe on a background thread.
  3. The engine installs the new code.
  4. The loop enters the new code at a safe point in the loop.

The replay shows all four as one change of tier. A counter does not rewrite the running code by itself.

Step through: a loop budget runs out and the loop moves up (OSR)
Step 0 of 24Ready
Your turn: follow the blue line

Step through a loop with a budget. This is a simple model of moving up a tier and OSR, not a view inside a real engine.

Running in
  1. script
Next: line 1
let interruptBudget = 18; function crunchLoop(limit) {  let total = 0;  for (let i = 1; i <= limit; i += 1) {    interruptBudget -= 3;    if (interruptBudget <= 0 && tier === "interpreter") {      tier = "baseline"; // model collapses request, compile, install, and OSR entry      interruptBudget = 18;    }    total += i;  }  return { tier, total };} console.log(crunchLoop(8));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Three plans: never optimize, optimize eagerly, or tier up

PLAYGROUND

The playground compares three plans on the same cost model:

  • Never optimize: every call runs in the interpreter.
  • Optimize eagerly: compile every function to the top tier before it runs. "Eagerly" means right away, before knowing whether it is needed.
  • Tiered JIT: start in the interpreter, and move a function up only when it crosses a threshold.

Try both programs:

  • In the short script, no function runs often enough to pay back a compile. Never optimizing costs the least, and the tiered JIT ties with it.
  • In the long session, one function, format, runs 160 times. The tiered JIT costs the least, because it compiles only format.
Never optimize, optimize eagerly, or tier up?
Three plans on one cost modelPop out in the code editor (opens in a new tab)JavaScript
const costs = {  interpreter: { compile: 0, call: 5 },  baseline: { compile: 15, call: 2 },  mid: { compile: 40, call: 1.2 },  top: { compile: 120, call: 0.6 },}; const program = [  { name: "route", calls: 3 },  { name: "format", calls: 160 },  { name: "validate", calls: 3 },  { name: "paint", calls: 3 },  { name: "cleanup", calls: 3 },]; // A real engine uses richer counters, feedback, and background compilation.// This model only shows the compile-time vs run-time trade-off.console.log("never optimize", 860);console.log("optimize eagerly", 703.2);console.log("tiered JIT", 422.6);
Long session: one function runs far more than the othersTiered JIT costs the least
Never optimize860.0

compile 0.0 · run 860.0 · functions in the top tier: 0

  1. interpret every call: 860.0
Optimize eagerly703.2

compile 600.0 · run 103.2 · functions in the top tier: 5

  1. compile every function: 600.0
  2. run all calls: 703.2
Tiered JIT422.6

compile 175.0 · run 247.6 · functions in the top tier: 1

  1. format -> baseline: 55.0
  2. format -> mid: 143.0
  3. format -> top: 335.0
Try it yourself

Tiered JIT costs the least for the long session: 175.0 units to compile and 247.6 units to run. This is a simple model. Real engines also use feedback, OSR, background compiles, and their own thresholds.

The playground adds made-up compile costs and costs per call. It shows the idea, not any engine's real thresholds.

Optimizing eagerly pays the top-tier compile cost of 120 for all five functions, even the ones called only 3 times. The tiered JIT pays compile costs only for functions that cross a threshold.

In a real program, other things can change which plan wins:

  • feedback that keeps changing;
  • compiled code that has to be thrown away;
  • compiles that have not finished yet;
  • limits on memory.

The idea that stays true: spend compile time where the running code shows it will pay back.

Real evidence from Node 22.23.1 and V8 12.4

EXPERIMENT

V8 has hidden testing functions whose names start with %. Node lets you use them only when you start it with the flag --allow-natives-syntax. A flag is an option you type after the command. These functions are not part of JavaScript. Without the flag, a browser or Node treats the % as a syntax error.

The lesson tests start separate Node processes to check real facts. First they remove the NODE_TEST_* settings, so the test runner does not change the child process. Then they check only facts that are the same on every run:

  • the version numbers;
  • which V8 options are on by default;
  • status bits from V8 12.4's runtime.h file;
  • lines from --trace-opt that mention TurboFan compiling.
V8 testing functions: for experiments, not for real appsJavaScript
// Node 22.23.1 (V8 12.4). Run only in Node with --allow-natives-syntax.function hotAdd(x) {  let total = 0;  for (let i = 0; i < 30; i += 1) total += (x + i) | 0;  return total;} %PrepareFunctionForOptimization(hotAdd);hotAdd(1);hotAdd(2);%OptimizeFunctionOnNextCall(hotAdd);hotAdd(3);console.log(%GetOptimizationStatus(hotAdd));
  • Line 1 says this runs only in Node with --allow-natives-syntax.
  • Line 8 tells V8 to start collecting feedback for hotAdd.
  • Line 11 asks V8 to optimize hotAdd on its next call. Line 12 makes that call.
  • Line 13 prints the optimization status, one number made of status bits.

A status bit is a yes-or-no answer packed into one number. In V8 12.4 these bits include:

  • kIsFunction = 1 << 0: this is a function;
  • kOptimized = 1 << 4: it has optimized code;
  • kTurboFanned = 1 << 6: that code came from TurboFan.

Forcing optimization this way gives the same result every time, so it works in a test. It is still a lab tool. Do not ship code that uses % functions or depends on a certain tier.

Run the probe and print what the optimizer doesShell
node --allow-natives-syntax --trace-opt tier-up-probe.mjs
The trace lines the lesson tests checkText
Node 22.23.1 (V8 12.4)
[manually marking ... hotAdd ... for optimization to TURBOFAN]
[compiling method ... hotAdd ... (target TURBOFAN)]
[completed compiling ... hotAdd ... (target TURBOFAN)]
status bits include kIsFunction, kOptimized, and kTurboFanned
What the Node build showed

The local Node 22.23.1 reports V8 12.4.254.21-node.56. Its options show:

  • Sparkplug and TurboFan are on by default.
  • Maglev exists as a flag, but it is off by default (--no-maglev).
  • The flags for building Maglev code on a background thread are there.

That is why the real optimization proof in this lesson uses TurboFan. The V8 section still teaches Maglev, because Chrome uses it.

Use tiering knowledge safely

PRACTICAL

Tiered compilation explains two things you will meet in real work:

  • Why a benchmark (a timing test) needs a warm-up. The first runs are slower while the engine is still moving code up the tiers.
  • Why short scripts often never reach a top tier.

It does not mean you should write code for Sparkplug, Maglev, TurboFan, Ion, or FTL. Engine teams change thresholds, tier names, and rules. Your job is to:

  • write clear code;
  • measure the real thing the user does;
  • warm up a benchmark before you time it;
  • expect the first few runs to differ from later runs.

Each row of the next table is a situation you may meet. Read across to see what tiering tells you and what to do.

What tiering means for everyday decisions
SituationWhat tiering tells youWhat to do
Benchmark warm-upThe first runs are slower while code moves up the tiers. Run the code a few times before timing, then report several timings or the middle one.Read Measuring performance before you trust a tiny benchmark.
Short scriptsA command-line tool's startup, or a page script that runs once, may never reach TurboFan or any top tier.Make loading and parsing faster before chasing top-tier speed.
Hot codeA function called very often, such as one that draws the page or reads data, can climb the tiers.Measure first. Later lessons on feedback explain why simple, steady inputs help.
Code for real usersDo not depend on % functions, tier flags, or one engine's thresholds.Write JavaScript that runs in every engine, and time what the user actually does.

If a benchmark changes after warm-up, see Measuring performance. If a hot function behaves differently after its inputs change, the next lessons on type feedback and inline caches explain what the optimizer looks at.

Common misconceptions

"JIT means everything is compiled right away."

No. A tiered JIT skips expensive compiling for cold code on purpose.

"Interpreters are useless, because compiled code is faster."

Interpreters help code start quickly. Running soon can matter more than running at top speed later.

"A function in TurboFan stays there forever."

Optimized code depends on guesses about the values it will see. If a guess turns out wrong, the engine can throw that code away and go back to a slower tier. Later lessons call this deoptimization.

"All engines use V8's tier names."

SpiderMonkey and JavaScriptCore have their own names, thresholds, and inner workings.

"The % functions are a way to speed up real apps."

They are V8 testing tools that need a special flag. Real code should be measured, not forced into a tier.

Each row of the next table is one idea. Read across to see what it means and what it does not mean.

Ideas that are easy to mix up
IdeaWhat it meansWhat it does not mean
JITCompiling while the program runs, based on what the code does.A promise that every function is compiled right away.
InterpreterA tier that starts cold code quickly.A useless fallback that modern engines avoid.
Optimized codeMachine code that depends on guesses about the values it will see.Code that stays forever and can never be thrown away.
V8 % functionsTesting tools that need a special flag, for experiments.JavaScript that works in every engine.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upAdd up the short run

What does this print for 4 calls in the interpreter?

Starter codePop out in the code editor (opens in a new tab)JavaScript
function totalTime(compileCost, callCost, calls) {
  return compileCost + callCost * calls;
}
console.log(totalTime(0, 5, 4));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFind the first move to baseline

    In the call-counter model, on which call does the function first move from the interpreter to baseline?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const thresholds = { baseline: 6, mid: 30, top: 90 };
    console.log(thresholds.baseline);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeRead the V8 status bit

      Which V8 status bit name means the code came from TurboFan?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeName the loop switch

        What is the short name for moving a running loop into compiled code without starting the function over?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeChoose safe code for a real app

          Production code is the code your real users run. Should it call V8's % optimization functions?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          // Should app code call %OptimizeFunctionOnNextCall in production?

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            8 QUESTIONS

            Keep the cost model, the tier names, and the counters in mind.

            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8Why do JavaScript engines use tiered compilation?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this trade-off snippet print first?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function totalTime(compileCost, callCost, calls) {
                return compileCost + callCost * calls;
              }
              console.log(totalTime(0, 5, 4));

              Choose an answer to see the explanation.

            3. Question 3 of 8Which V8 tier is the baseline compiler that does not optimize, announced in 2021?

              Choose an answer to see the explanation.

            4. Question 4 of 8Which sentence best describes Maglev in V8?

              Choose an answer to see the explanation.

            5. Question 5 of 8What makes SpiderMonkey's warm-up counter go up?

              Choose an answer to see the explanation.

            6. Question 6 of 8What does this loop-budget model print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              let tier = "interpreter";
              let budget = 6;
              for (let i = 0; i < 3; i += 1) {
                budget -= 3;
                if (budget <= 0) tier = "baseline";
              }
              console.log(tier);

              Choose an answer to see the explanation.

            7. Question 7 of 8Why should production code (the code your real users run) avoid V8's % functions, such as %GetOptimizationStatus?

              Choose an answer to see the explanation.

            8. Question 8 of 8Which benchmark habit comes from tiered compilation?

              Choose an answer to see the explanation.

            Key takeaways

            • An interpreter starts quickly. A compiler makes faster code, but costs time first. A JIT compiler decides, while the program runs, what is worth compiling.
            • V8's tiers are Ignition, Sparkplug, Maglev, and TurboFan. TurboFan's back end is moving from Sea of Nodes to Turboshaft.
            • SpiderMonkey and JavaScriptCore use other names but the same plan: spend more compile time on hotter code.
            • Engines spot hot code by counting calls and loop steps, and by feedback about values. OSR lets a running loop switch tiers.
            • Warm up benchmarks, expect short scripts to stay in cheap tiers, and never write real app code for one engine tier.

            Remember the one-liner.
            Tiered compilation means: start cheap, watch the code run, and spend compile time only where it pays back.

            Up next: Type feedback & feedback vectors.

            CompleteFrontend Clear concepts. Working examples.