javascript
exercises
exercises.js⚡javascript
/**
* ============================================================
* 22.1 ENGINE ARCHITECTURE - EXERCISES
* ============================================================
*
* Practice your understanding of JavaScript engine internals.
*/
/**
* EXERCISE 1: IDENTIFYING HOT CODE
*
* Analyze this code and identify which functions would become
* "hot" (optimized by JIT) and why.
*
* Instructions:
* 1. Add comments explaining which functions get optimized
* 2. Explain why some functions might not get optimized
*/
function processData(data) {
return data.map((item) => transform(item));
}
function transform(item) {
return item * 2 + 1;
}
function oneTimeSetup() {
console.log('Setting up...');
return { initialized: true };
}
function handleClick(event) {
console.log('Click handled');
}
// Main execution
const data = Array.from({ length: 10000 }, (_, i) => i);
const result = processData(data);
oneTimeSetup();
// YOUR ANALYSIS HERE:
// Which functions become hot and why?
// function processData:
// function transform:
// function oneTimeSetup:
// function handleClick:
/**
* EXERCISE 2: TYPE STABILITY
*
* Refactor these functions to be more optimization-friendly
* by ensuring type stability.
*/
// REFACTOR THIS: Function has unstable types
function calculateBadly(input) {
if (typeof input === 'string') {
return parseInt(input) * 2;
} else if (typeof input === 'number') {
return input * 2;
} else if (Array.isArray(input)) {
return input.length * 2;
}
return null;
}
// YOUR SOLUTION: Create separate, type-stable functions
function calculateNumber(input) {
// TODO: Implement for numbers only
}
function calculateFromString(input) {
// TODO: Implement for strings only
}
function calculateFromArray(input) {
// TODO: Implement for arrays only
}
// Test your solutions
// console.log(calculateNumber(5)); // Should return 10
// console.log(calculateFromString("5")); // Should return 10
// console.log(calculateFromArray([1,2,3])); // Should return 6
/**
* EXERCISE 3: IDENTIFYING DEOPTIMIZATION TRIGGERS
*
* Find the deoptimization triggers in this code and fix them.
*/
// PROBLEMATIC CODE - Find and fix the issues
function processUsers(users) {
const results = [];
for (let i = 0; i < users.length; i++) {
try {
const user = users[i];
// Issue 1: Type inconsistency
let score;
if (user.premium) {
score = 'VIP'; // String
} else {
score = user.points; // Number
}
// Issue 2: Using arguments
function logDetails() {
console.log(arguments[0], arguments[1]);
}
// Issue 3: Deleting properties
const userCopy = { ...user };
if (!user.active) {
delete userCopy.lastLogin;
}
results.push({ score, userCopy });
} catch (e) {
results.push(null);
}
}
return results;
}
// YOUR FIXED VERSION:
function processUsersOptimized(users) {
// TODO: Rewrite without deoptimization triggers
}
/**
* EXERCISE 4: LOOP OPTIMIZATION UNDERSTANDING
*
* Identify the optimization opportunities in this loop
* and write an optimized version.
*/
function processArraySlow(arr, config) {
const results = [];
for (let i = 0; i < arr.length; i++) {
// What's the issue here?
const multiplier = config.baseMultiplier * config.factor;
const offset = Math.sqrt(config.offset);
// What's the issue here?
results.push(arr[i] * multiplier + offset);
}
return results;
}
// YOUR OPTIMIZED VERSION:
function processArrayFast(arr, config) {
// TODO: Apply loop invariant code motion
}
// Test
const testConfig = { baseMultiplier: 2, factor: 3, offset: 16 };
const testArr = [1, 2, 3, 4, 5];
// console.log(processArrayFast(testArr, testConfig));
/**
* EXERCISE 5: FUNCTION INLINING CANDIDATES
*
* Identify which functions are good candidates for inlining
* and create a manually inlined version.
*/
// Original functions
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function complexCalculation(x, y, z) {
const sum = add(x, y);
const product = multiply(sum, z);
const final = add(product, x);
return final;
}
// YOUR INLINED VERSION:
function complexCalculationInlined(x, y, z) {
// TODO: Inline all function calls
}
// Verify both produce same results
// console.log("Original:", complexCalculation(2, 3, 4));
// console.log("Inlined:", complexCalculationInlined(2, 3, 4));
/**
* EXERCISE 6: ESCAPE ANALYSIS
*
* Determine which objects can be eliminated by escape analysis
* and which must be heap-allocated.
*/
function scenario1(x, y) {
const point = { x, y };
return point; // Does point escape?
}
function scenario2(x, y) {
const point = { x, y };
return point.x + point.y; // Does point escape?
}
function scenario3(x, y) {
const point = { x, y };
globalPoints.push(point); // Does point escape?
return point.x;
}
function scenario4(x, y) {
const point = { x, y };
const distance = Math.sqrt(point.x ** 2 + point.y ** 2);
return distance; // Does point escape?
}
const globalPoints = [];
// YOUR ANSWERS:
// scenario1: Escapes because...
// scenario2: Does NOT escape because...
// scenario3: Escapes because...
// scenario4: Does NOT escape because...
/**
* EXERCISE 7: DEAD CODE IDENTIFICATION
*
* Mark all the dead code in this function.
*/
function processValue(x) {
const a = x * 2;
const b = x * 3; // Dead code?
const c = x * 4; // Dead code?
if (x > 0) {
const d = a + 10;
const e = a - 10; // Dead code?
return d;
}
return a;
const f = x * 5; // Dead code?
return f;
}
// YOUR ANALYSIS:
// List all dead code lines and explain why
/**
* EXERCISE 8: BYTECODE MENTAL MODEL
*
* Given this JavaScript function, write pseudocode
* representing the bytecode operations.
*/
function example(x, y) {
if (x > y) {
return x - y;
} else {
return y - x;
}
}
// YOUR PSEUDOCODE:
/*
BYTECODE for example(x, y):
// TODO: Write bytecode-like pseudocode
// Use operations like:
// - Ldar (load to accumulator)
// - Star (store accumulator)
// - TestGreaterThan
// - JumpIfFalse
// - Sub (subtract)
// - Return
*/
/**
* EXERCISE 9: OPTIMIZATION-FRIENDLY OBJECT CREATION
*
* Refactor this object creation pattern to be more
* optimization-friendly (consistent hidden classes).
*/
// PROBLEMATIC: Creates objects with different shapes
function createUserBad(name, age, email, isAdmin) {
const user = {};
user.name = name;
if (age) {
user.age = age;
}
if (email) {
user.email = email;
}
if (isAdmin) {
user.isAdmin = isAdmin;
user.permissions = ['read', 'write', 'delete'];
}
return user;
}
// YOUR SOLUTION: All objects should have same shape
function createUserGood(name, age, email, isAdmin) {
// TODO: Create objects with consistent hidden class
}
// Test
// console.log(createUserGood("Alice", 25, "alice@example.com", true));
// console.log(createUserGood("Bob", null, null, false));
/**
* EXERCISE 10: BENCHMARK IMPLEMENTATION
*
* Implement a proper benchmark function that accounts
* for JIT warmup and provides accurate measurements.
*/
function createBenchmark(targetFn) {
return {
warmup(iterations = 10000) {
// TODO: Run warmup iterations
},
run(iterations = 100000) {
// TODO: Run benchmark and return results
// Return: { totalTime, opsPerSecond, avgTimePerOp }
},
compare(otherFn, iterations = 100000) {
// TODO: Compare two functions
// Return: { fn1Results, fn2Results, winner, speedup }
},
};
}
// Test your benchmark
function fn1(x) {
return x * 2;
}
function fn2(x) {
return x + x;
}
// const benchmark = createBenchmark(fn1);
// benchmark.warmup();
// console.log(benchmark.run());
// console.log(benchmark.compare(fn2));
/**
* EXERCISE 11: ENGINE BEHAVIOR PREDICTION
*
* Predict what happens in the engine for each scenario.
*/
// Scenario A
function scenarioA() {
function add(a, b) {
return a + b;
}
for (let i = 0; i < 100000; i++) {
add(i, i + 1); // What happens to 'add'?
}
add('hello', 'world'); // What happens now?
}
// Scenario B
function scenarioB() {
const arr = [];
for (let i = 0; i < 1000; i++) {
arr.push({ x: i, y: i * 2 }); // Hidden class behavior?
}
arr.push({ y: 100, x: 50 }); // What's different?
}
// Scenario C
function scenarioC() {
function getValue(obj) {
return obj.value;
}
const obj1 = { value: 1, extra: 2 };
const obj2 = { value: 2 };
const obj3 = { different: 3, value: 4 };
getValue(obj1); // IC state after this?
getValue(obj2); // IC state after this?
getValue(obj3); // IC state after this?
}
// YOUR PREDICTIONS:
// Scenario A:
// Scenario B:
// Scenario C:
/**
* EXERCISE 12: MINI PARSER
*
* Implement a simple tokenizer for a subset of JavaScript.
* This helps understand the first stage of parsing.
*/
function tokenize(code) {
const tokens = [];
let current = 0;
while (current < code.length) {
let char = code[current];
// Skip whitespace
if (/\s/.test(char)) {
current++;
continue;
}
// TODO: Handle different token types:
// - Numbers (0-9)
// - Identifiers (a-z, A-Z, _)
// - Operators (+, -, *, /, =)
// - Punctuation (;, {, }, (, ))
// - Keywords (let, const, function, return)
// Example for numbers:
if (/[0-9]/.test(char)) {
let value = '';
while (/[0-9]/.test(char)) {
value += char;
char = code[++current];
}
tokens.push({ type: 'NUMBER', value });
continue;
}
// TODO: Add more token types
current++;
}
return tokens;
}
// Test your tokenizer
// console.log(tokenize("let x = 10 + 5;"));
// Expected: [
// { type: 'KEYWORD', value: 'let' },
// { type: 'IDENTIFIER', value: 'x' },
// { type: 'OPERATOR', value: '=' },
// { type: 'NUMBER', value: '10' },
// { type: 'OPERATOR', value: '+' },
// { type: 'NUMBER', value: '5' },
// { type: 'PUNCTUATION', value: ';' }
// ]
/**
* ============================================================
* SOLUTIONS (Check after attempting)
* ============================================================
*/
// Scroll down for solutions...
// SOLUTION 2: Type Stability
function calculateNumberSolution(input) {
return input * 2;
}
function calculateFromStringSolution(input) {
return parseInt(input, 10) * 2;
}
function calculateFromArraySolution(input) {
return input.length * 2;
}
// SOLUTION 4: Loop Optimization
function processArrayFastSolution(arr, config) {
// Hoist invariant calculations out of the loop
const multiplier = config.baseMultiplier * config.factor;
const offset = Math.sqrt(config.offset);
const results = [];
const len = arr.length; // Cache length
for (let i = 0; i < len; i++) {
results.push(arr[i] * multiplier + offset);
}
return results;
}
// SOLUTION 5: Function Inlining
function complexCalculationInlinedSolution(x, y, z) {
const sum = x + y; // Inlined add
const product = sum * z; // Inlined multiply
const final = product + x; // Inlined add
return final;
}
// SOLUTION 9: Optimization-Friendly Object Creation
function createUserGoodSolution(name, age, email, isAdmin) {
return {
name: name,
age: age ?? null,
email: email ?? null,
isAdmin: Boolean(isAdmin),
permissions: isAdmin ? ['read', 'write', 'delete'] : null,
};
}
// SOLUTION 10: Benchmark Implementation
function createBenchmarkSolution(targetFn) {
let isWarmedUp = false;
return {
warmup(iterations = 10000) {
for (let i = 0; i < iterations; i++) {
targetFn(i);
}
isWarmedUp = true;
return this;
},
run(iterations = 100000) {
if (!isWarmedUp) {
this.warmup();
}
const start = performance.now();
for (let i = 0; i < iterations; i++) {
targetFn(i);
}
const totalTime = performance.now() - start;
return {
totalTime: totalTime.toFixed(2) + 'ms',
opsPerSecond: ((iterations / totalTime) * 1000).toFixed(0),
avgTimePerOp: ((totalTime / iterations) * 1000000).toFixed(2) + 'ns',
};
},
compare(otherFn, iterations = 100000) {
const fn1Results = this.run(iterations);
// Warmup other function
for (let i = 0; i < 10000; i++) {
otherFn(i);
}
const start = performance.now();
for (let i = 0; i < iterations; i++) {
otherFn(i);
}
const totalTime = performance.now() - start;
const fn2Results = {
totalTime: totalTime.toFixed(2) + 'ms',
opsPerSecond: ((iterations / totalTime) * 1000).toFixed(0),
avgTimePerOp: ((totalTime / iterations) * 1000000).toFixed(2) + 'ns',
};
const fn1Speed = parseFloat(fn1Results.opsPerSecond);
const fn2Speed = parseFloat(fn2Results.opsPerSecond);
return {
fn1Results,
fn2Results,
winner: fn1Speed > fn2Speed ? 'fn1' : 'fn2',
speedup:
(Math.max(fn1Speed, fn2Speed) / Math.min(fn1Speed, fn2Speed)).toFixed(
2
) + 'x',
};
},
};
}
// SOLUTION 12: Mini Parser
function tokenizeSolution(code) {
const tokens = [];
let current = 0;
const keywords = [
'let',
'const',
'function',
'return',
'if',
'else',
'for',
'while',
];
while (current < code.length) {
let char = code[current];
if (/\s/.test(char)) {
current++;
continue;
}
if (/[0-9]/.test(char)) {
let value = '';
while (/[0-9]/.test(char)) {
value += char;
char = code[++current];
}
tokens.push({ type: 'NUMBER', value });
continue;
}
if (/[a-zA-Z_]/.test(char)) {
let value = '';
while (/[a-zA-Z0-9_]/.test(char)) {
value += char;
char = code[++current];
}
const type = keywords.includes(value) ? 'KEYWORD' : 'IDENTIFIER';
tokens.push({ type, value });
continue;
}
if (/[+\-*/=<>!]/.test(char)) {
tokens.push({ type: 'OPERATOR', value: char });
current++;
continue;
}
if (/[;{}(),]/.test(char)) {
tokens.push({ type: 'PUNCTUATION', value: char });
current++;
continue;
}
current++;
}
return tokens;
}
console.log('Exercises loaded. Start solving!');