javascript

examples

examples.js
/**
 * ============================================================
 * 22.5 PERFORMANCE OPTIMIZATION INTERNALS - EXAMPLES
 * ============================================================
 *
 * Demonstrating optimization patterns, deoptimization triggers,
 * and engine-friendly coding practices.
 */

/**
 * ============================================================
 * EXAMPLE 1: TYPE STABILITY
 * ============================================================
 */

console.log('=== Example 1: Type Stability ===\n');

// Monomorphic function - same types always
function addNumbersStable(a, b) {
  return a + b;
}

// Simulate many calls with consistent types
console.log('Monomorphic calls (all numbers):');
let sum = 0;
for (let i = 0; i < 5; i++) {
  sum = addNumbersStable(sum, i);
  console.log(`  Call ${i + 1}: addNumbersStable(${sum - i}, ${i}) = ${sum}`);
}

console.log('\nThis is FAST because:');
console.log('  - Engine knows a and b are always numbers');
console.log('  - Can generate optimized machine code');
console.log('  - No type checks needed at runtime\n');

// Now show what happens with mixed types
function addMixed(a, b) {
  return a + b;
}

console.log('Polymorphic calls (mixed types):');
console.log(`  addMixed(1, 2) = ${addMixed(1, 2)}`);
console.log(`  addMixed("hello", "world") = ${addMixed('hello', 'world')}`);
console.log(`  addMixed([1], [2]) = ${addMixed([1], [2])}`);
console.log(`  addMixed({a:1}, "") = ${addMixed({ a: 1 }, '')}`);

console.log('\nThis is SLOW because:');
console.log('  - Engine must handle multiple type combinations');
console.log('  - Cannot optimize for specific types');
console.log('  - Inline cache becomes megamorphic\n');

/**
 * ============================================================
 * EXAMPLE 2: HIDDEN CLASS CONSISTENCY
 * ============================================================
 */

console.log('=== Example 2: Hidden Class Consistency ===\n');

// ✅ Good: Consistent initialization order
class GoodPoint {
  constructor(x, y) {
    this.x = x; // Always x first
    this.y = y; // Always y second
  }
}

const goodPoints = [];
for (let i = 0; i < 3; i++) {
  goodPoints.push(new GoodPoint(i, i * 2));
}

console.log('Good: All points have same hidden class');
console.log('Points:', goodPoints.map((p) => `(${p.x}, ${p.y})`).join(', '));

// ❌ Bad: Inconsistent initialization
function createBadPoint(x, y, hasZ) {
  const point = {};
  if (hasZ) {
    point.z = 0; // Sometimes z is added first
  }
  point.x = x;
  point.y = y;
  return point;
}

const badPoints = [
  createBadPoint(1, 2, false),
  createBadPoint(3, 4, true),
  createBadPoint(5, 6, false),
];

console.log('\nBad: Points have different hidden classes');
console.log('Point 1 keys:', Object.keys(badPoints[0]));
console.log('Point 2 keys:', Object.keys(badPoints[1]));
console.log('Point 3 keys:', Object.keys(badPoints[2]));

// ✅ Fixed: Always initialize all properties
function createFixedPoint(x, y, hasZ) {
  return {
    x,
    y,
    z: hasZ ? 0 : undefined, // Always has z
  };
}

console.log('\nFixed: All points have same shape');
const fixedPoints = [
  createFixedPoint(1, 2, false),
  createFixedPoint(3, 4, true),
  createFixedPoint(5, 6, false),
];
console.log('All have same keys:', Object.keys(fixedPoints[0]).join(', '));
console.log();

/**
 * ============================================================
 * EXAMPLE 3: DEOPTIMIZATION TRIGGERS
 * ============================================================
 */

console.log('=== Example 3: Deoptimization Triggers ===\n');

// Simulate optimization tracking
class OptimizationTracker {
  constructor() {
    this.callCount = 0;
    this.seenTypes = new Set();
    this.status = 'cold';
  }

  recordCall(type) {
    this.callCount++;
    const previousSize = this.seenTypes.size;
    this.seenTypes.add(type);

    if (this.callCount >= 3 && this.seenTypes.size === 1) {
      this.status = 'optimized';
    }

    if (previousSize > 0 && this.seenTypes.size > previousSize) {
      if (this.status === 'optimized') {
        console.log(`  ⚠️ DEOPT! New type '${type}' caused deoptimization`);
        this.status = 'deoptimized';
      }
    }
  }

  getStatus() {
    return `Status: ${this.status} (${this.callCount} calls, ${this.seenTypes.size} types)`;
  }
}

function demonstrateDeopt() {
  const tracker = new OptimizationTracker();

  console.log('Calling function with numbers:');
  for (let i = 0; i < 5; i++) {
    tracker.recordCall('number');
    console.log(`  Call ${i + 1}: ${tracker.getStatus()}`);
  }

  console.log('\nFunction is now optimized for numbers...');
  console.log('Calling with a string:');
  tracker.recordCall('string');
  console.log(`  ${tracker.getStatus()}`);
}

demonstrateDeopt();
console.log();

/**
 * ============================================================
 * EXAMPLE 4: HOT AND COLD CODE SEPARATION
 * ============================================================
 */

console.log('=== Example 4: Hot/Cold Code Separation ===\n');

// ❌ Bad: Error handling mixed with hot path
function processBad(items) {
  const results = [];
  for (const item of items) {
    try {
      if (typeof item !== 'number') {
        console.error(`Invalid item: ${item}`);
        const error = new Error('Invalid type');
        error.item = item;
        error.timestamp = Date.now();
        // More cold code in hot path...
        results.push(null);
        continue;
      }
      results.push(item * 2);
    } catch (e) {
      results.push(null);
    }
  }
  return results;
}

// ✅ Good: Cold code separated
function handleInvalid(item) {
  // Cold path - rarely executed
  console.error(`Invalid item: ${item}`);
  const error = new Error('Invalid type');
  error.item = item;
  error.timestamp = Date.now();
  return null;
}

function processGood(items) {
  const results = [];
  for (const item of items) {
    if (typeof item !== 'number') {
      results.push(handleInvalid(item)); // Cold code moved out
      continue;
    }
    results.push(item * 2); // Hot path stays clean
  }
  return results;
}

const testItems = [1, 2, 'oops', 3, null, 4];
console.log('Bad version (cold code inline):');
console.log('  Results:', processBad(testItems));

console.log('\nGood version (cold code separated):');
console.log('  Results:', processGood(testItems));
console.log();

/**
 * ============================================================
 * EXAMPLE 5: OBJECT POOLING
 * ============================================================
 */

console.log('=== Example 5: Object Pooling ===\n');

// Without pooling - creates garbage
function particleSystemBad(particleCount, frames) {
  let totalCreated = 0;

  for (let frame = 0; frame < frames; frame++) {
    for (let i = 0; i < particleCount; i++) {
      // New object every frame!
      const particle = {
        x: Math.random() * 100,
        y: Math.random() * 100,
        vx: Math.random() * 2 - 1,
        vy: Math.random() * 2 - 1,
      };
      totalCreated++;
      // particle is garbage after this scope
    }
  }

  return totalCreated;
}

// With pooling - reuses objects
class ParticlePool {
  constructor(size) {
    this.pool = [];
    this.active = [];

    for (let i = 0; i < size; i++) {
      this.pool.push({ x: 0, y: 0, vx: 0, vy: 0 });
    }

    this.allocated = size;
    this.reuses = 0;
  }

  acquire() {
    if (this.pool.length === 0) {
      this.allocated++;
      return { x: 0, y: 0, vx: 0, vy: 0 };
    }
    this.reuses++;
    return this.pool.pop();
  }

  release(particle) {
    particle.x = 0;
    particle.y = 0;
    particle.vx = 0;
    particle.vy = 0;
    this.pool.push(particle);
  }

  getStats() {
    return {
      allocated: this.allocated,
      reuses: this.reuses,
      inPool: this.pool.length,
    };
  }
}

function particleSystemGood(particleCount, frames, pool) {
  for (let frame = 0; frame < frames; frame++) {
    const frameParticles = [];

    for (let i = 0; i < particleCount; i++) {
      const particle = pool.acquire();
      particle.x = Math.random() * 100;
      particle.y = Math.random() * 100;
      particle.vx = Math.random() * 2 - 1;
      particle.vy = Math.random() * 2 - 1;
      frameParticles.push(particle);
    }

    // Return particles at end of frame
    for (const particle of frameParticles) {
      pool.release(particle);
    }
  }

  return pool.getStats();
}

console.log('Without pooling:');
const badCreated = particleSystemBad(100, 10);
console.log(`  Objects created: ${badCreated} (all become garbage)\n`);

console.log('With pooling:');
const pool = new ParticlePool(100);
const stats = particleSystemGood(100, 10, pool);
console.log(`  Objects allocated: ${stats.allocated}`);
console.log(`  Object reuses: ${stats.reuses}`);
console.log(`  Objects in pool: ${stats.inPool}`);
console.log();

/**
 * ============================================================
 * EXAMPLE 6: ARRAY OPTIMIZATION PATTERNS
 * ============================================================
 */

console.log('=== Example 6: Array Optimization Patterns ===\n');

// Pre-sizing arrays
console.log('Pre-sizing vs Dynamic growth:');

function dynamicGrowth(size) {
  const arr = [];
  for (let i = 0; i < size; i++) {
    arr.push(i); // May cause reallocation
  }
  return arr;
}

function preSized(size) {
  const arr = new Array(size);
  for (let i = 0; i < size; i++) {
    arr[i] = i; // No reallocation
  }
  return arr;
}

console.log('  Dynamic growth: array starts empty, grows as needed');
console.log('  Pre-sized: array allocated once at full size');

// Holey arrays vs packed arrays
console.log('\nHoley vs Packed arrays:');

const packed = [1, 2, 3, 4, 5];
console.log('  Packed: [1, 2, 3, 4, 5]');
console.log('    - Contiguous memory');
console.log('    - Fast iteration');

const holey = [1, , 3, , 5]; // Missing elements
console.log('  Holey: [1, , 3, , 5]');
console.log('    - Has holes (missing indices)');
console.log('    - Slower iteration (must check for holes)');

// Avoid making packed arrays holey
const wasPackedNowHoley = [1, 2, 3, 4, 5];
delete wasPackedNowHoley[2]; // Now holey!
console.log('\n  delete arr[2] makes array holey - avoid this!');

// SMI arrays (Small Integer)
console.log('\nSMI vs Double arrays:');
const smiArray = [1, 2, 3, 4, 5]; // All small integers
console.log('  SMI array: [1, 2, 3, 4, 5]');
console.log('    - Fastest for integer operations');

const doubleArray = [1.1, 2.2, 3.3]; // Contains floats
console.log('  Double array: [1.1, 2.2, 3.3]');
console.log('    - Uses 64-bit floats');

const mixedArray = [1, 'two', 3]; // Mixed types
console.log("  Mixed array: [1, 'two', 3]");
console.log('    - Slowest - must box values');
console.log();

/**
 * ============================================================
 * EXAMPLE 7: FUNCTION INLINING
 * ============================================================
 */

console.log('=== Example 7: Function Inlining ===\n');

// Small functions can be inlined
function double(x) {
  return x * 2;
}

function triple(x) {
  return x * 3;
}

// The engine might inline these into the loop
function processWithSmallFunctions(arr) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(double(triple(arr[i])));
  }
  return result;
}

console.log('Small functions like double(x) and triple(x)');
console.log('can be inlined by the engine:\n');
console.log('  Original: result.push(double(triple(arr[i])))');
console.log('  Inlined:  result.push((arr[i] * 3) * 2)\n');

// Large functions are less likely to be inlined
function largeFunction(x) {
  // Many operations...
  let result = x;
  result = result * 2;
  result = result + 10;
  result = Math.sqrt(result);
  result = Math.round(result);
  // More operations...
  return result;
}

console.log('Large functions with many operations');
console.log('are less likely to be inlined.\n');

// Demonstrate the concept
const testArr = [1, 2, 3, 4, 5];
console.log('Input:', testArr);
console.log('After double(triple(x)):', processWithSmallFunctions(testArr));
console.log();

/**
 * ============================================================
 * EXAMPLE 8: STRING CONCATENATION PATTERNS
 * ============================================================
 */

console.log('=== Example 8: String Concatenation ===\n');

// Bad: Repeated concatenation
function badConcat(n) {
  let result = '';
  for (let i = 0; i < n; i++) {
    result += 'item' + i + ','; // Creates many intermediate strings
  }
  return result;
}

// Good: Array join
function goodConcat(n) {
  const parts = [];
  for (let i = 0; i < n; i++) {
    parts.push('item' + i);
  }
  return parts.join(',');
}

// Good: Template literals for simple cases
function templateConcat(name, age) {
  return `Name: ${name}, Age: ${age}`;
}

console.log('Repeated concatenation (bad for large strings):');
console.log(`  result += 'item' + i + ','`);
console.log('  Creates intermediate string objects\n');

console.log('Array.join (good for building large strings):');
console.log(`  parts.push(...); return parts.join(',')`);
console.log('  Single final string allocation\n');

console.log('Template literals (good for interpolation):');
console.log('  `Name: ${name}, Age: ${age}`');
console.log('  Efficient for simple cases\n');

console.log('Example outputs:');
console.log('  badConcat(5):', badConcat(5));
console.log('  goodConcat(5):', goodConcat(5));
console.log("  templateConcat('Alice', 30):", templateConcat('Alice', 30));
console.log();

/**
 * ============================================================
 * EXAMPLE 9: AVOIDING MEGAMORPHIC INLINE CACHES
 * ============================================================
 */

console.log('=== Example 9: Inline Cache States ===\n');

// Simulate IC behavior
class InlineCacheSimulator {
  constructor(name) {
    this.name = name;
    this.seenShapes = new Map();
    this.state = 'uninitialized';
  }

  access(shape) {
    const count = (this.seenShapes.get(shape) || 0) + 1;
    this.seenShapes.set(shape, count);

    const shapeCount = this.seenShapes.size;

    if (shapeCount === 1) {
      this.state = 'monomorphic';
    } else if (shapeCount <= 4) {
      this.state = 'polymorphic';
    } else {
      this.state = 'megamorphic';
    }

    return this.state;
  }

  report() {
    console.log(`  IC '${this.name}': ${this.state}`);
    console.log(`    Shapes seen: ${[...this.seenShapes.keys()].join(', ')}`);
  }
}

// Monomorphic - all same shape
const icMono = new InlineCacheSimulator('obj.x (mono)');
console.log('Accessing obj.x on same-shaped objects:');
icMono.access('{x,y}');
icMono.access('{x,y}');
icMono.access('{x,y}');
icMono.report();

// Polymorphic - few different shapes
const icPoly = new InlineCacheSimulator('obj.x (poly)');
console.log('\nAccessing obj.x on different-shaped objects:');
icPoly.access('{x}');
icPoly.access('{x,y}');
icPoly.access('{x,y,z}');
icPoly.report();

// Megamorphic - many different shapes
const icMega = new InlineCacheSimulator('obj.x (mega)');
console.log('\nAccessing obj.x on many different shapes:');
icMega.access('{x}');
icMega.access('{x,y}');
icMega.access('{x,y,z}');
icMega.access('{a,x}');
icMega.access('{b,x}');
icMega.access('{c,x}');
icMega.report();

console.log('\nPerformance impact:');
console.log('  Monomorphic: Direct lookup, very fast');
console.log('  Polymorphic: Check few shapes, still fast');
console.log('  Megamorphic: Hash lookup, slowest');
console.log();

/**
 * ============================================================
 * EXAMPLE 10: TYPED ARRAYS FOR PERFORMANCE
 * ============================================================
 */

console.log('=== Example 10: Typed Arrays ===\n');

// Regular array
const regularArray = [1.5, 2.5, 3.5, 4.5, 5.5];

// Typed array
const typedArray = new Float64Array([1.5, 2.5, 3.5, 4.5, 5.5]);

console.log('Regular Array:');
console.log('  [1.5, 2.5, 3.5, 4.5, 5.5]');
console.log('  - Each element can be any type');
console.log('  - Values are boxed as Number objects');
console.log('  - More memory, less predictable\n');

console.log('Float64Array:');
console.log('  new Float64Array([1.5, 2.5, 3.5, 4.5, 5.5])');
console.log('  - Fixed to 64-bit floats');
console.log('  - Direct memory representation');
console.log('  - More efficient for numeric operations\n');

// Different typed arrays for different needs
console.log('Typed Array Options:');
console.log('  Int8Array:    -128 to 127');
console.log('  Uint8Array:   0 to 255');
console.log('  Int16Array:   -32768 to 32767');
console.log('  Uint16Array:  0 to 65535');
console.log('  Int32Array:   -2B to 2B');
console.log('  Uint32Array:  0 to 4B');
console.log('  Float32Array: 32-bit floats');
console.log('  Float64Array: 64-bit floats\n');

// Memory comparison
console.log('Memory usage comparison (5 numbers):');
console.log('  Regular array: ~80+ bytes (boxed values + array overhead)');
console.log('  Float64Array: 40 bytes (8 bytes × 5)');
console.log('  Int32Array: 20 bytes (4 bytes × 5)');
console.log('  Int8Array: 5 bytes (1 byte × 5)');
console.log();

/**
 * ============================================================
 * EXAMPLE 11: AVOIDING OPTIMIZATION KILLERS
 * ============================================================
 */

console.log('=== Example 11: Optimization Killers ===\n');

// ❌ eval() prevents optimization
console.log('1. eval() - prevents lexical scope optimization');
console.log("   ❌ eval('x + y') // Engine can't predict scope\n");

// ❌ with statement
console.log('2. with statement - unpredictable scope');
console.log('   ❌ with(obj) { x = 1 } // x could be obj.x or outer x\n');

// ❌ arguments object issues
console.log('3. arguments object leaking');
console.log('   ❌ return arguments // Leaks arguments object');
console.log('   ✅ return [...arguments] // Copy instead\n');

// ❌ Modifying arguments
console.log('4. Modifying arguments object');
console.log('   ❌ arguments[0] = 10 // Causes deopt');
console.log('   ✅ const args = [...arguments]; args[0] = 10\n');

// ❌ delete operator
console.log('5. delete operator on objects');
console.log('   ❌ delete obj.x // Changes hidden class');
console.log('   ✅ obj.x = undefined // Preserves shape\n');

// ❌ for-in on arrays
console.log('6. for-in on arrays');
console.log('   ❌ for (let i in array) // Iterates all enumerable');
console.log('   ✅ for (let i of array) // Direct iteration\n');

// ✅ Good patterns
console.log('Good patterns:');

function goodPatterns() {
  // Use rest parameters instead of arguments
  const safeVariadic = (...args) => args;

  // Set to undefined instead of delete
  const clearProperty = (obj, prop) => {
    obj[prop] = undefined;
  };

  // Use for-of for arrays
  const sumArray = (arr) => {
    let sum = 0;
    for (const val of arr) sum += val;
    return sum;
  };

  return { safeVariadic, clearProperty, sumArray };
}

const patterns = goodPatterns();
console.log('  safeVariadic(1, 2, 3):', patterns.safeVariadic(1, 2, 3));
console.log('  sumArray([1, 2, 3, 4, 5]):', patterns.sumArray([1, 2, 3, 4, 5]));
console.log();

/**
 * ============================================================
 * EXAMPLE 12: PERFORMANCE MEASUREMENT
 * ============================================================
 */

console.log('=== Example 12: Performance Measurement ===\n');

// Simple performance measurement utility
class PerformanceMeasure {
  static measure(name, fn, iterations = 1000) {
    // Warm-up runs
    for (let i = 0; i < 10; i++) fn();

    const start = performance.now();

    for (let i = 0; i < iterations; i++) {
      fn();
    }

    const end = performance.now();
    const total = end - start;
    const perCall = total / iterations;

    return { name, total, perCall, iterations };
  }

  static compare(name1, fn1, name2, fn2, iterations = 1000) {
    const result1 = this.measure(name1, fn1, iterations);
    const result2 = this.measure(name2, fn2, iterations);

    const faster = result1.perCall < result2.perCall ? name1 : name2;
    const ratio =
      Math.max(result1.perCall, result2.perCall) /
      Math.min(result1.perCall, result2.perCall);

    return {
      result1,
      result2,
      faster,
      ratio: ratio.toFixed(2),
    };
  }

  static formatResult(result) {
    return `${result.name}: ${result.total.toFixed(
      2
    )}ms total, ${result.perCall.toFixed(4)}ms/call`;
  }
}

// Compare array iteration methods
const testArray = Array.from({ length: 1000 }, (_, i) => i);

console.log('Comparing array iteration methods:\n');

const forLoopResult = PerformanceMeasure.measure('for loop', () => {
  let sum = 0;
  for (let i = 0; i < testArray.length; i++) {
    sum += testArray[i];
  }
  return sum;
});

const forOfResult = PerformanceMeasure.measure('for-of', () => {
  let sum = 0;
  for (const val of testArray) {
    sum += val;
  }
  return sum;
});

const forEachResult = PerformanceMeasure.measure('forEach', () => {
  let sum = 0;
  testArray.forEach((val) => {
    sum += val;
  });
  return sum;
});

const reduceResult = PerformanceMeasure.measure('reduce', () => {
  return testArray.reduce((sum, val) => sum + val, 0);
});

console.log(PerformanceMeasure.formatResult(forLoopResult));
console.log(PerformanceMeasure.formatResult(forOfResult));
console.log(PerformanceMeasure.formatResult(forEachResult));
console.log(PerformanceMeasure.formatResult(reduceResult));

console.log('\nNote: Results vary by engine and context.');
console.log('Always measure in YOUR specific use case!\n');

/**
 * ============================================================
 * EXAMPLE 13: BRANCH PREDICTION
 * ============================================================
 */

console.log('=== Example 13: Branch Prediction ===\n');

// Sorted data is faster due to branch prediction
function sumConditional(arr, threshold) {
  let sum = 0;
  for (const val of arr) {
    if (val < threshold) {
      sum += val;
    }
  }
  return sum;
}

console.log('Branch prediction effect:');
console.log('  When data is sorted, branches are more predictable.');
console.log('  CPU can speculate correctly more often.\n');

const unsortedData = Array.from({ length: 1000 }, () => Math.random() * 100);
const sortedData = [...unsortedData].sort((a, b) => a - b);

console.log('With unsorted data:');
console.log('  Branch pattern: random (if true, if false, if true...)\n');

console.log('With sorted data:');
console.log(
  '  Branch pattern: consistent (if true, true, true... then false, false...)'
);
console.log("  CPU can predict: 'probably same as last time'\n");

/**
 * ============================================================
 * SUMMARY
 * ============================================================
 */

console.log('=== Performance Optimization Summary ===\n');

console.log('Key Takeaways:');
console.log('  1. Type Stability: Keep function arguments consistent');
console.log(
  '  2. Shape Consistency: Initialize object properties in same order'
);
console.log('  3. Hot/Cold Separation: Keep hot paths clean');
console.log('  4. Object Pooling: Reuse objects in loops');
console.log('  5. Array Optimization: Pre-size, avoid holes');
console.log('  6. Function Inlining: Keep hot functions small');
console.log('  7. String Building: Use array.join() for large strings');
console.log('  8. IC Optimization: Avoid megamorphic operations');
console.log('  9. Typed Arrays: Use for numeric data');
console.log(' 10. Avoid Killers: No eval(), with, delete');
console.log(' 11. Measure: Profile before optimizing');
console.log(' 12. Branch Prediction: Sorted data can be faster\n');

console.log('=== Examples Complete ===\n');
Examples - JavaScript Tutorial | DeepML