javascript
examples
examples.js⚡javascript
/**
* ============================================================
* 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');