javascript
exercises
exercises.js⚡javascript
/**
* ============================================================
* 22.5 PERFORMANCE OPTIMIZATION INTERNALS - EXERCISES
* ============================================================
*
* Practice identifying and fixing performance anti-patterns,
* understanding deoptimization, and writing engine-friendly code.
*/
/**
* EXERCISE 1: IDENTIFY DEOPTIMIZATION CAUSES
*
* Analyze the following functions and identify what will cause
* deoptimization. Then fix them.
*/
console.log('=== Exercise 1: Identify Deoptimization Causes ===\n');
// Problem 1: Type instability
function calculateValue(input) {
if (typeof input === 'string') {
return input.length;
}
if (typeof input === 'number') {
return input * 2;
}
if (Array.isArray(input)) {
return input.length;
}
return 0;
}
// TODO: Identify the problem and create type-specific functions
// calculateNumberValue, calculateStringValue, calculateArrayValue
// Problem 2: Shape inconsistency
function createUser(name, email, isAdmin) {
const user = {};
user.name = name;
if (email) {
user.email = email;
}
if (isAdmin) {
user.role = 'admin';
user.permissions = ['read', 'write', 'delete'];
}
return user;
}
// TODO: Fix to ensure all users have the same shape
// Problem 3: Arguments leaking
function logAllArguments() {
console.log('Arguments:', arguments);
return arguments; // Leaking!
}
// TODO: Fix using rest parameters
console.log('Fix the functions above, then verify:');
console.log(' calculateValue(5):', calculateValue(5));
console.log(
" createUser('Alice', 'a@b.com', true):",
createUser('Alice', 'a@b.com', true)
);
console.log(
" createUser('Bob', null, false):",
createUser('Bob', null, false)
);
console.log();
/**
* EXERCISE 2: OPTIMIZE OBJECT CREATION
*
* Refactor the object creation patterns to be more engine-friendly.
*/
console.log('=== Exercise 2: Optimize Object Creation ===\n');
// Bad: Random property order
function createPointBad(x, y, z) {
const point = {};
if (z !== undefined) {
point.z = z;
}
point.x = x;
point.y = y;
return point;
}
// TODO: Create createPointGood that always has same shape
// Bad: Adding properties after construction
class VectorBad {
constructor(x, y) {
this.x = x;
this.y = y;
}
addZ(z) {
this.z = z; // Changes shape!
return this;
}
}
// TODO: Create VectorGood that has consistent shape
// Test your solutions
console.log('Verify your solutions maintain consistent shapes');
console.log();
/**
* EXERCISE 3: OPTIMIZE ARRAY OPERATIONS
*
* Fix the array operations to avoid common pitfalls.
*/
console.log('=== Exercise 3: Optimize Array Operations ===\n');
// Problem 1: Creating holey array
function createSequenceBad(n) {
const arr = [];
arr[n - 1] = n - 1; // Creates holey array!
for (let i = 0; i < n - 1; i++) {
arr[i] = i;
}
return arr;
}
// TODO: Create createSequenceGood that avoids holes
// Problem 2: Type mixing
function processNumbersBad(numbers) {
const results = [];
for (const n of numbers) {
if (n < 0) {
results.push('negative'); // String in number array!
} else {
results.push(n * 2);
}
}
return results;
}
// TODO: Create processNumbersGood that keeps consistent types
// Problem 3: Delete in array
function removeMiddleBad(arr) {
const mid = Math.floor(arr.length / 2);
delete arr[mid]; // Creates hole!
return arr;
}
// TODO: Create removeMiddleGood using splice
console.log('Test your array optimizations:');
console.log(' createSequenceBad(5):', createSequenceBad(5));
console.log(' processNumbersBad([1, -2, 3]):', processNumbersBad([1, -2, 3]));
console.log();
/**
* EXERCISE 4: IMPLEMENT OBJECT POOL
*
* Create an object pool for particle system optimization.
*/
console.log('=== Exercise 4: Implement Object Pool ===\n');
class ParticlePool {
constructor(size) {
this.pool = [];
this.activeCount = 0;
// TODO: Pre-allocate particles with shape { x, y, vx, vy, life }
}
/**
* Get a particle from the pool
* @returns {Object} A particle object
*/
acquire() {
// TODO: Return a particle from pool, or create new if empty
// Track activeCount
return { x: 0, y: 0, vx: 0, vy: 0, life: 0 };
}
/**
* Return a particle to the pool
* @param {Object} particle - The particle to return
*/
release(particle) {
// TODO: Reset particle properties and return to pool
// Update activeCount
}
/**
* Get pool statistics
*/
getStats() {
return {
poolSize: this.pool.length,
active: this.activeCount,
total: this.pool.length + this.activeCount,
};
}
}
// Test the pool
const pool = new ParticlePool(10);
console.log('Initial stats:', pool.getStats());
const particles = [];
for (let i = 0; i < 5; i++) {
particles.push(pool.acquire());
}
console.log('After acquiring 5:', pool.getStats());
particles.forEach((p) => pool.release(p));
console.log('After releasing all:', pool.getStats());
console.log();
/**
* EXERCISE 5: HOT/COLD CODE SEPARATION
*
* Refactor to separate hot and cold code paths.
*/
console.log('=== Exercise 5: Hot/Cold Code Separation ===\n');
// Bad: Cold code mixed with hot code
function processItemsBad(items) {
const results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
// Hot path
const value = item.value * 2;
// Cold path mixed in (validation, error handling)
if (typeof item.value !== 'number') {
console.error(`Invalid item at index ${i}:`, item);
const error = new Error(`Expected number, got ${typeof item.value}`);
error.item = item;
error.index = i;
// Maybe log to server, notify admin, etc.
results.push({ error: true, index: i });
continue;
}
if (value > 1000) {
console.warn(`Large value at index ${i}:`, value);
// Additional logging, metrics, etc.
}
results.push({ value, index: i });
}
return results;
}
// TODO: Create processItemsGood with hot/cold separation
// Move error handling and logging to separate functions
function processItemsGood(items) {
// TODO: Implement with clean hot path
return [];
}
// Helper functions for cold paths
// TODO: Create handleInvalidItem(item, index)
// TODO: Create handleLargeValue(value, index)
console.log('Test hot/cold separation:');
const testItems = [
{ value: 10 },
{ value: 'invalid' },
{ value: 600 },
{ value: 50 },
];
console.log('Bad version:', processItemsBad(testItems));
console.log();
/**
* EXERCISE 6: MONOMORPHIC FUNCTION DESIGN
*
* Refactor polymorphic functions to be monomorphic.
*/
console.log('=== Exercise 6: Monomorphic Function Design ===\n');
// Bad: Polymorphic - handles many types
function getLength(thing) {
if (typeof thing === 'string') {
return thing.length;
}
if (Array.isArray(thing)) {
return thing.length;
}
if (thing instanceof Map || thing instanceof Set) {
return thing.size;
}
if (typeof thing === 'object' && thing !== null) {
return Object.keys(thing).length;
}
return 0;
}
// TODO: Create type-specific monomorphic functions:
// getStringLength(str)
// getArrayLength(arr)
// getMapSize(map)
// getSetSize(set)
// getObjectKeyCount(obj)
console.log('Polymorphic (bad):');
console.log(" getLength('hello'):", getLength('hello'));
console.log(' getLength([1,2,3]):', getLength([1, 2, 3]));
console.log(' getLength({a:1,b:2}):', getLength({ a: 1, b: 2 }));
console.log('\nCreate monomorphic alternatives:');
console.log(" getStringLength('hello')");
console.log(' getArrayLength([1,2,3])');
console.log(' getObjectKeyCount({a:1,b:2})');
console.log();
/**
* EXERCISE 7: STRING BUILDING OPTIMIZATION
*
* Optimize string concatenation for large outputs.
*/
console.log('=== Exercise 7: String Building Optimization ===\n');
// Bad: Repeated concatenation
function buildHTMLBad(items) {
let html = '<ul>';
for (const item of items) {
html += '<li>';
html += item.name;
html += ': ';
html += item.value;
html += '</li>';
}
html += '</ul>';
return html;
}
// TODO: Create buildHTMLGood using array.join()
// TODO: Create buildHTMLTemplate using template literals efficiently
const testData = [
{ name: 'Item 1', value: 100 },
{ name: 'Item 2', value: 200 },
{ name: 'Item 3', value: 300 },
];
console.log('Bad version:', buildHTMLBad(testData));
console.log('Implement good version using array.join()');
console.log();
/**
* EXERCISE 8: INLINE CACHE OPTIMIZATION
*
* Fix code to maintain monomorphic inline caches.
*/
console.log('=== Exercise 8: Inline Cache Optimization ===\n');
// Bad: Different object shapes in same operation
function sumValuesBad(objects) {
let sum = 0;
for (const obj of objects) {
sum += obj.value; // IC becomes polymorphic/megamorphic
}
return sum;
}
// Objects with different shapes
const badObjects = [
{ value: 1 },
{ value: 2, extra: 'data' },
{ type: 'special', value: 3 },
{ value: 4, nested: { data: true } },
];
// TODO: Create normalizeObjects(objects) that returns objects with same shape
// Then create sumValuesGood that works on normalized objects
console.log('Bad: Mixed shapes cause megamorphic IC');
console.log(' Sum:', sumValuesBad(badObjects));
console.log('\nFix by normalizing object shapes first');
console.log();
/**
* EXERCISE 9: TYPED ARRAY OPERATIONS
*
* Use typed arrays for numeric operations.
*/
console.log('=== Exercise 9: Typed Array Operations ===\n');
// Bad: Regular array for numeric data
function processCoordinatesBad(coords) {
// coords is [[x, y], [x, y], ...]
const results = [];
for (const [x, y] of coords) {
const distance = Math.sqrt(x * x + y * y);
results.push(distance);
}
return results;
}
// TODO: Create processCoordinatesGood using Float64Array
// Input: Float64Array of alternating x, y values
// Output: Float64Array of distances
// TODO: Create functions:
// coordsToTypedArray(coords) - converts [[x,y], ...] to Float64Array
// typedArrayToCoords(typedArr) - converts back
console.log('Regular array version:');
const coords = [
[3, 4],
[5, 12],
[8, 15],
];
console.log(' Input:', coords);
console.log(' Distances:', processCoordinatesBad(coords));
console.log('\nImplement typed array version for better performance');
console.log();
/**
* EXERCISE 10: PERFORMANCE BENCHMARKING
*
* Create a benchmarking utility to compare implementations.
*/
console.log('=== Exercise 10: Performance Benchmarking ===\n');
class Benchmark {
/**
* Run a function multiple times and measure performance
* @param {Function} fn - Function to benchmark
* @param {number} iterations - Number of iterations
* @param {number} warmup - Warmup iterations
* @returns {Object} - Timing statistics
*/
static run(fn, iterations = 1000, warmup = 100) {
// TODO: Implement benchmarking
// 1. Run warmup iterations (don't measure)
// 2. Run measured iterations
// 3. Calculate min, max, average, total
return {
iterations,
total: 0,
average: 0,
min: 0,
max: 0,
};
}
/**
* Compare two functions
* @param {string} name1 - Name of first function
* @param {Function} fn1 - First function
* @param {string} name2 - Name of second function
* @param {Function} fn2 - Second function
* @param {number} iterations - Number of iterations
* @returns {Object} - Comparison results
*/
static compare(name1, fn1, name2, fn2, iterations = 1000) {
// TODO: Run both benchmarks and compare
// Return which is faster and by how much
return {
winner: name1,
ratio: 1,
};
}
/**
* Format benchmark results as string
* @param {string} name - Benchmark name
* @param {Object} results - Benchmark results
* @returns {string} - Formatted string
*/
static format(name, results) {
// TODO: Create readable output
return `${name}: ${results.total}ms`;
}
}
// Test the benchmark
console.log('Benchmark utility created. Test with:');
console.log(' const result = Benchmark.run(() => arrayOperation());');
console.log(" console.log(Benchmark.format('Operation', result));");
console.log();
/**
* ============================================================
* SOLUTIONS
* ============================================================
*/
console.log('=== SOLUTIONS ===\n');
// Solution 1: Type-specific functions
console.log('--- Solution 1: Type-Specific Functions ---\n');
function calculateNumberValue(input) {
return input * 2;
}
function calculateStringValue(input) {
return input.length;
}
function calculateArrayValue(input) {
return input.length;
}
// Fixed user creation with consistent shape
function createUserFixed(name, email, isAdmin) {
return {
name: name,
email: email ?? null,
role: isAdmin ? 'admin' : 'user',
permissions: isAdmin ? ['read', 'write', 'delete'] : ['read'],
};
}
// Fixed arguments using rest params
function logAllArgumentsFixed(...args) {
console.log('Arguments:', args);
return args;
}
console.log('Type-specific functions:');
console.log(' calculateNumberValue(5):', calculateNumberValue(5));
console.log(" calculateStringValue('hello'):", calculateStringValue('hello'));
console.log('\nConsistent shape:');
console.log(' Admin:', createUserFixed('Alice', 'a@b.com', true));
console.log(' User:', createUserFixed('Bob', null, false));
console.log();
// Solution 2: Object creation
console.log('--- Solution 2: Optimized Object Creation ---\n');
function createPointGood(x, y, z) {
return {
x: x,
y: y,
z: z !== undefined ? z : null,
};
}
class VectorGood {
constructor(x, y, z = null) {
this.x = x;
this.y = y;
this.z = z; // Always has z
}
setZ(z) {
this.z = z;
return this;
}
}
console.log('createPointGood(1, 2):', createPointGood(1, 2));
console.log('createPointGood(1, 2, 3):', createPointGood(1, 2, 3));
console.log('new VectorGood(1, 2):', new VectorGood(1, 2));
console.log();
// Solution 3: Array optimization
console.log('--- Solution 3: Array Optimization ---\n');
function createSequenceGood(n) {
const arr = new Array(n);
for (let i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
function processNumbersGood(numbers) {
const results = [];
for (const n of numbers) {
if (n < 0) {
results.push(n * -2); // Keep as number
} else {
results.push(n * 2);
}
}
return results;
}
function removeMiddleGood(arr) {
const mid = Math.floor(arr.length / 2);
arr.splice(mid, 1);
return arr;
}
console.log('createSequenceGood(5):', createSequenceGood(5));
console.log('processNumbersGood([1, -2, 3]):', processNumbersGood([1, -2, 3]));
console.log(
'removeMiddleGood([1,2,3,4,5]):',
removeMiddleGood([1, 2, 3, 4, 5])
);
console.log();
// Solution 4: Object Pool
console.log('--- Solution 4: Object Pool ---\n');
class ParticlePoolSolution {
constructor(size) {
this.pool = [];
this.activeCount = 0;
for (let i = 0; i < size; i++) {
this.pool.push({ x: 0, y: 0, vx: 0, vy: 0, life: 0 });
}
}
acquire() {
this.activeCount++;
if (this.pool.length > 0) {
return this.pool.pop();
}
return { x: 0, y: 0, vx: 0, vy: 0, life: 0 };
}
release(particle) {
particle.x = 0;
particle.y = 0;
particle.vx = 0;
particle.vy = 0;
particle.life = 0;
this.pool.push(particle);
this.activeCount--;
}
getStats() {
return {
poolSize: this.pool.length,
active: this.activeCount,
total: this.pool.length + this.activeCount,
};
}
}
const poolSol = new ParticlePoolSolution(10);
console.log('Initial:', poolSol.getStats());
const p1 = poolSol.acquire();
const p2 = poolSol.acquire();
console.log('After 2 acquires:', poolSol.getStats());
poolSol.release(p1);
console.log('After 1 release:', poolSol.getStats());
console.log();
// Solution 5: Hot/Cold Separation
console.log('--- Solution 5: Hot/Cold Separation ---\n');
function handleInvalidItemSol(item, index) {
console.error(`Invalid item at index ${index}:`, item);
return { error: true, index };
}
function handleLargeValueSol(value, index) {
console.warn(`Large value at index ${index}:`, value);
}
function processItemsGoodSol(items) {
const results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (typeof item.value !== 'number') {
results.push(handleInvalidItemSol(item, i));
continue;
}
const value = item.value * 2;
if (value > 1000) {
handleLargeValueSol(value, i);
}
results.push({ value, index: i });
}
return results;
}
console.log('Hot/cold separated version works the same');
console.log('but keeps hot path clean for optimization');
console.log();
// Solution 6: Monomorphic functions
console.log('--- Solution 6: Monomorphic Functions ---\n');
const getStringLength = (str) => str.length;
const getArrayLength = (arr) => arr.length;
const getMapSize = (map) => map.size;
const getSetSize = (set) => set.size;
const getObjectKeyCount = (obj) => Object.keys(obj).length;
console.log("getStringLength('hello'):", getStringLength('hello'));
console.log('getArrayLength([1,2,3]):', getArrayLength([1, 2, 3]));
console.log('getObjectKeyCount({a:1,b:2}):', getObjectKeyCount({ a: 1, b: 2 }));
console.log();
// Solution 7: String building
console.log('--- Solution 7: String Building ---\n');
function buildHTMLGood(items) {
const parts = ['<ul>'];
for (const item of items) {
parts.push(`<li>${item.name}: ${item.value}</li>`);
}
parts.push('</ul>');
return parts.join('');
}
console.log('buildHTMLGood:', buildHTMLGood(testData));
console.log();
// Solution 8: IC optimization
console.log('--- Solution 8: IC Optimization ---\n');
function normalizeObjects(objects) {
return objects.map((obj) => ({
value: obj.value,
extra: null,
type: null,
nested: null,
}));
}
function sumValuesGood(objects) {
let sum = 0;
for (const obj of objects) {
sum += obj.value;
}
return sum;
}
const normalizedObjects = normalizeObjects(badObjects);
console.log('Normalized objects all have same shape');
console.log('Sum:', sumValuesGood(normalizedObjects));
console.log();
// Solution 9: Typed arrays
console.log('--- Solution 9: Typed Arrays ---\n');
function coordsToTypedArray(coords) {
const arr = new Float64Array(coords.length * 2);
for (let i = 0; i < coords.length; i++) {
arr[i * 2] = coords[i][0];
arr[i * 2 + 1] = coords[i][1];
}
return arr;
}
function processCoordinatesGood(typedCoords) {
const count = typedCoords.length / 2;
const distances = new Float64Array(count);
for (let i = 0; i < count; i++) {
const x = typedCoords[i * 2];
const y = typedCoords[i * 2 + 1];
distances[i] = Math.sqrt(x * x + y * y);
}
return distances;
}
const typedCoords = coordsToTypedArray(coords);
const distances = processCoordinatesGood(typedCoords);
console.log('Typed array input:', typedCoords);
console.log('Distances:', distances);
console.log();
// Solution 10: Benchmarking
console.log('--- Solution 10: Benchmarking ---\n');
class BenchmarkSolution {
static run(fn, iterations = 1000, warmup = 100) {
// Warmup
for (let i = 0; i < warmup; i++) {
fn();
}
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
const end = performance.now();
times.push(end - start);
}
const total = times.reduce((a, b) => a + b, 0);
return {
iterations,
total,
average: total / iterations,
min: Math.min(...times),
max: Math.max(...times),
};
}
static compare(name1, fn1, name2, fn2, iterations = 1000) {
const result1 = this.run(fn1, iterations);
const result2 = this.run(fn2, iterations);
const winner = result1.average < result2.average ? name1 : name2;
const ratio =
Math.max(result1.average, result2.average) /
Math.min(result1.average, result2.average);
return {
[name1]: result1,
[name2]: result2,
winner,
ratio: ratio.toFixed(2),
};
}
static format(name, results) {
return (
`${name}: total=${results.total.toFixed(2)}ms, ` +
`avg=${results.average.toFixed(4)}ms, ` +
`min=${results.min.toFixed(4)}ms, ` +
`max=${results.max.toFixed(4)}ms`
);
}
}
const testFn = () => {
let sum = 0;
for (let i = 0; i < 100; i++) sum += i;
return sum;
};
const benchResult = BenchmarkSolution.run(testFn, 100);
console.log(BenchmarkSolution.format('Test function', benchResult));
console.log();
console.log('=== Exercises Complete ===\n');