javascript

exercises

exercises.js
/**
 * ============================================================
 * 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!');
Exercises - JavaScript Tutorial | DeepML