The simulator below models a classic tiered JIT pipeline. A loop runs repeatedly; you control the iteration count and can inject a type surprise mid-run.
<!-- JIT compilation demo - shows interpreter → baseline JIT → optimizing JIT → deopt cycle -->
<div class="jit-demo">
<div class="controls">
<label for="iters">Iterations: <strong id="iters-val">200</strong></label>
<input id="iters" type="range" min="50" max="500" value="200" step="50">
<button id="run-btn" type="button">Run simulation</button>
<button id="deopt-btn" type="button" class="ghost">Inject type change</button>
<button id="reset-btn" type="button" class="ghost">Reset</button>
</div>
<div id="chart" class="chart" aria-label="Bar chart showing throughput per iteration colored by JIT tier"></div>
<div id="status" class="status"></div>
<div class="legend">
<span class="dot interp"></span> Interpreter
<span class="dot baseline"></span> Baseline JIT
<span class="dot opt"></span> Optimizing JIT
<span class="dot deopt"></span> Deoptimized
</div>
</div>
/* Styles for JIT demo - bar chart colored by execution tier */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.jit-demo { display: flex; flex-direction: column; gap: .6rem; }
.controls { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
label { font-size: .88rem; color: #444; white-space: nowrap; }
input[type=range] { width: 120px; accent-color: #1d6fa5; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
.chart { position: relative; height: 210px; border: 1px solid #d0d8df;
border-radius: 8px; background: #f9fbfc; overflow: hidden; }
/* tier color bands */
.band { position: absolute; left: 0; right: 0; opacity: .06; }
.bar { position: absolute; bottom: 0; width: 3px; border-radius: 2px 2px 0 0;
transition: height .05s; }
.bar.interp { background: #6c757d; }
.bar.baseline { background: #1d6fa5; }
.bar.opt { background: #0a7d33; }
.bar.deopt { background: #c92f3c; }
.tier-label { position: absolute; left: 6px; font-size: .7rem; color: #888;
font-weight: 600; letter-spacing: .04em; }
.status { font-size: .9rem; font-weight: 600; min-height: 1.3em; color: #1d3557; }
.status.ok { color: #0a7d33; }
.status.deopt { color: #c92f3c; }
.legend { display: flex; flex-wrap: wrap; gap: .5rem .9rem; font-size: .8rem; color: #555; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 3px; }
.dot.interp { background: #6c757d; }
.dot.baseline { background: #1d6fa5; }
.dot.opt { background: #0a7d33; }
.dot.deopt { background: #c92f3c; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* JIT compilation simulator - models interpreter → tiered JIT → deoptimization */
/* Tier definitions with relative speed multipliers */
const TIERS = {
interp: { cls: 'interp', label: 'Interpreter', speed: 1.0 },
baseline: { cls: 'baseline', label: 'Baseline JIT', speed: 4.5 },
opt: { cls: 'opt', label: 'Optimizing JIT', speed: 10.0 },
deopt: { cls: 'deopt', label: 'Deoptimizing', speed: 1.0 }
};
/* Iteration thresholds for tier transitions */
const WARMUP_BASELINE = 50;
const WARMUP_OPT = 120;
const DEOPT_RECOVERY = 30;
const CHART_H = 200;
const MAX_SPEED = 10.5;
let running = false, bars = [], deoptPending = false;
const chart = document.getElementById('chart');
const statusEl = document.getElementById('status');
const runBtn = document.getElementById('run-btn');
const deoptBtn = document.getElementById('deopt-btn');
const resetBtn = document.getElementById('reset-btn');
const itersIn = document.getElementById('iters');
const itersVal = document.getElementById('iters-val');
itersIn.addEventListener('input', () => { itersVal.textContent = itersIn.value; });
function addTierLabels() {
/* Draw horizontal reference lines for each tier */
chart.innerHTML = '';
const labels = [
{ y: CHART_H * (1 - 1.0 / MAX_SPEED), text: 'Interpreter' },
{ y: CHART_H * (1 - 4.5 / MAX_SPEED), text: 'Baseline JIT' },
{ y: CHART_H * (1 - 10.0 / MAX_SPEED), text: 'Optimizing JIT' }
];
for (const { y, text } of labels) {
const l = document.createElement('div');
l.className = 'tier-label';
l.style.top = Math.max(0, y - 14) + 'px';
l.textContent = text;
chart.appendChild(l);
}
}
function addBar(tier, speed) {
/* Append one bar for this iteration */
const b = document.createElement('div');
const pct = speed / MAX_SPEED;
const h = Math.round(pct * CHART_H);
b.className = 'bar ' + tier;
b.style.height = h + 'px';
b.style.left = (bars.length * 5) + 'px';
chart.appendChild(b);
bars.push(b);
/* Scroll by removing oldest bar when chart is full */
const maxBars = Math.floor(chart.clientWidth / 5);
if (bars.length > maxBars) {
bars[0].remove();
bars.shift();
for (let i = 0; i < bars.length; i++) bars[i].style.left = (i * 5) + 'px';
}
}
function setStatus(text, cls) {
statusEl.textContent = text;
statusEl.className = 'status ' + (cls || '');
}
async function runSim() {
if (running) return;
running = true;
deoptPending = false;
runBtn.disabled = true;
deoptBtn.disabled = false;
bars = [];
addTierLabels();
const totalIters = parseInt(itersIn.value, 10);
let tier = 'interp';
let deoptAt = -1;
let i = 0;
function step() {
if (i >= totalIters) {
/* Simulation complete */
running = false;
runBtn.disabled = false;
deoptBtn.disabled = true;
const tierInfo = TIERS[tier === 'deopt' ? 'interp' : tier];
setStatus(i18nFmt('Done — %n% iterations, final tier: %tier%.', { n: totalIters, tier: tierInfo.label }), 'ok');
return;
}
/* Advance tier or trigger deopt based on iteration count */
if (deoptPending && tier !== 'deopt') {
tier = 'deopt';
deoptAt = i;
setStatus('Type surprise! Deoptimizing…', 'deopt');
deoptPending = false;
} else if (tier === 'deopt' && i - deoptAt >= DEOPT_RECOVERY) {
tier = 'interp';
deoptAt = -1;
} else if (tier === 'interp' && i >= WARMUP_BASELINE) {
tier = 'baseline';
setStatus('Baseline JIT compiled — faster now.', '');
} else if (tier === 'baseline' && i >= WARMUP_OPT) {
tier = 'opt';
setStatus('Optimizing JIT active — peak speed!', 'ok');
}
/* Add small random noise so bars look like real profiling data */
const baseSpeed = TIERS[tier].speed;
const noise = (Math.random() - 0.5) * 0.4;
const speed = Math.max(0.2, baseSpeed + noise);
addBar(TIERS[tier].cls, speed);
i++;
setTimeout(step, 18);
}
setStatus('Running in interpreter mode…', '');
step();
}
deoptBtn.addEventListener('click', () => {
if (!running) return;
deoptPending = true;
});
resetBtn.addEventListener('click', () => {
running = false;
deoptPending = false;
bars = [];
runBtn.disabled = false;
deoptBtn.disabled = true;
addTierLabels();
setStatus('', '');
});
deoptBtn.disabled = true;
addTierLabels();
setStatus('Press "Run simulation" to start.', '');
<!-- JIT compilation demo - shows interpreter → baseline JIT → optimizing JIT → deopt cycle -->
<div class="jit-demo">
<div class="controls">
<label for="iters">Iterations: <strong id="iters-val">200</strong></label>
<input id="iters" type="range" min="50" max="500" value="200" step="50">
<button id="run-btn" type="button">Run simulation</button>
<button id="deopt-btn" type="button" class="ghost">Inject type change</button>
<button id="reset-btn" type="button" class="ghost">Reset</button>
</div>
<div id="chart" class="chart" aria-label="Bar chart showing throughput per iteration colored by JIT tier"></div>
<div id="status" class="status"></div>
<div class="legend">
<span class="dot interp"></span> Interpreter
<span class="dot baseline"></span> Baseline JIT
<span class="dot opt"></span> Optimizing JIT
<span class="dot deopt"></span> Deoptimized
</div>
</div>
/* Styles for JIT demo - bar chart colored by execution tier */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.jit-demo { display: flex; flex-direction: column; gap: .6rem; }
.controls { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
label { font-size: .88rem; color: #444; white-space: nowrap; }
input[type=range] { width: 120px; accent-color: #1d6fa5; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
.chart { position: relative; height: 210px; border: 1px solid #d0d8df;
border-radius: 8px; background: #f9fbfc; overflow: hidden; }
/* tier color bands */
.band { position: absolute; left: 0; right: 0; opacity: .06; }
.bar { position: absolute; bottom: 0; width: 3px; border-radius: 2px 2px 0 0;
transition: height .05s; }
.bar.interp { background: #6c757d; }
.bar.baseline { background: #1d6fa5; }
.bar.opt { background: #0a7d33; }
.bar.deopt { background: #c92f3c; }
.tier-label { position: absolute; left: 6px; font-size: .7rem; color: #888;
font-weight: 600; letter-spacing: .04em; }
.status { font-size: .9rem; font-weight: 600; min-height: 1.3em; color: #1d3557; }
.status.ok { color: #0a7d33; }
.status.deopt { color: #c92f3c; }
.legend { display: flex; flex-wrap: wrap; gap: .5rem .9rem; font-size: .8rem; color: #555; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 3px; }
.dot.interp { background: #6c757d; }
.dot.baseline { background: #1d6fa5; }
.dot.opt { background: #0a7d33; }
.dot.deopt { background: #c92f3c; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* JIT compilation simulator - models interpreter → tiered JIT → deoptimization */
/* Tier definitions with relative speed multipliers */
const TIERS = {
interp: { cls: 'interp', label: 'Interpreter', speed: 1.0 },
baseline: { cls: 'baseline', label: 'Baseline JIT', speed: 4.5 },
opt: { cls: 'opt', label: 'Optimizing JIT', speed: 10.0 },
deopt: { cls: 'deopt', label: 'Deoptimizing', speed: 1.0 }
};
/* Iteration thresholds for tier transitions */
const WARMUP_BASELINE = 50;
const WARMUP_OPT = 120;
const DEOPT_RECOVERY = 30;
const CHART_H = 200;
const MAX_SPEED = 10.5;
let running = false, bars = [], deoptPending = false;
const chart = document.getElementById('chart');
const statusEl = document.getElementById('status');
const runBtn = document.getElementById('run-btn');
const deoptBtn = document.getElementById('deopt-btn');
const resetBtn = document.getElementById('reset-btn');
const itersIn = document.getElementById('iters');
const itersVal = document.getElementById('iters-val');
itersIn.addEventListener('input', () => { itersVal.textContent = itersIn.value; });
function addTierLabels() {
/* Draw horizontal reference lines for each tier */
chart.innerHTML = '';
const labels = [
{ y: CHART_H * (1 - 1.0 / MAX_SPEED), text: 'Interpreter' },
{ y: CHART_H * (1 - 4.5 / MAX_SPEED), text: 'Baseline JIT' },
{ y: CHART_H * (1 - 10.0 / MAX_SPEED), text: 'Optimizing JIT' }
];
for (const { y, text } of labels) {
const l = document.createElement('div');
l.className = 'tier-label';
l.style.top = Math.max(0, y - 14) + 'px';
l.textContent = text;
chart.appendChild(l);
}
}
function addBar(tier, speed) {
/* Append one bar for this iteration */
const b = document.createElement('div');
const pct = speed / MAX_SPEED;
const h = Math.round(pct * CHART_H);
b.className = 'bar ' + tier;
b.style.height = h + 'px';
b.style.left = (bars.length * 5) + 'px';
chart.appendChild(b);
bars.push(b);
/* Scroll by removing oldest bar when chart is full */
const maxBars = Math.floor(chart.clientWidth / 5);
if (bars.length > maxBars) {
bars[0].remove();
bars.shift();
for (let i = 0; i < bars.length; i++) bars[i].style.left = (i * 5) + 'px';
}
}
function setStatus(text, cls) {
statusEl.textContent = text;
statusEl.className = 'status ' + (cls || '');
}
async function runSim() {
if (running) return;
running = true;
deoptPending = false;
runBtn.disabled = true;
deoptBtn.disabled = false;
bars = [];
addTierLabels();
const totalIters = parseInt(itersIn.value, 10);
let tier = 'interp';
let deoptAt = -1;
let i = 0;
function step() {
if (i >= totalIters) {
/* Simulation complete */
running = false;
runBtn.disabled = false;
deoptBtn.disabled = true;
const tierInfo = TIERS[tier === 'deopt' ? 'interp' : tier];
setStatus(i18nFmt('Done — %n% iterations, final tier: %tier%.', { n: totalIters, tier: tierInfo.label }), 'ok');
return;
}
/* Advance tier or trigger deopt based on iteration count */
if (deoptPending && tier !== 'deopt') {
tier = 'deopt';
deoptAt = i;
setStatus('Type surprise! Deoptimizing…', 'deopt');
deoptPending = false;
} else if (tier === 'deopt' && i - deoptAt >= DEOPT_RECOVERY) {
tier = 'interp';
deoptAt = -1;
} else if (tier === 'interp' && i >= WARMUP_BASELINE) {
tier = 'baseline';
setStatus('Baseline JIT compiled — faster now.', '');
} else if (tier === 'baseline' && i >= WARMUP_OPT) {
tier = 'opt';
setStatus('Optimizing JIT active — peak speed!', 'ok');
}
/* Add small random noise so bars look like real profiling data */
const baseSpeed = TIERS[tier].speed;
const noise = (Math.random() - 0.5) * 0.4;
const speed = Math.max(0.2, baseSpeed + noise);
addBar(TIERS[tier].cls, speed);
i++;
setTimeout(step, 18);
}
setStatus('Running in interpreter mode…', '');
step();
}
deoptBtn.addEventListener('click', () => {
if (!running) return;
deoptPending = true;
});
resetBtn.addEventListener('click', () => {
running = false;
deoptPending = false;
bars = [];
runBtn.disabled = false;
deoptBtn.disabled = true;
addTierLabels();
setStatus('', '');
});
deoptBtn.disabled = true;
addTierLabels();
setStatus('Press "Run simulation" to start.', '');
Notice the pattern: Interpreter is slow and predictable. After enough iterations the JIT compiler kicks in and throughput jumps dramatically. Inject a type change and watch the runtime deoptimize — throughput drops back to interpreter speed while the JIT relearns the new pattern, then climbs again. This warmup-compile-deoptimize cycle is exactly what V8, HotSpot, and SpiderMonkey do millions of times per second in real programs.
Comments
Loading comments...