JavaScript Performance Cheat Sheet

JavaScript Performance Cheatsheet

Performance Measurement

Performance API

// Measure execution time
const start = performance.now();
expensiveOperation();
const end = performance.now();
console.log(`Operation took ${end - start} milliseconds`);

// Performance marks and measures
performance.mark('fetchStart');
fetch('/api/data').then(() => {
    performance.mark('fetchEnd');
    performance.measure('fetch', 'fetchStart', 'fetchEnd');
    
    const measure = performance.getEntriesByName('fetch')[0];
    console.log(`Fetch took ${measure.duration}ms`);
});

// Memory usage (Node.js)
const memUsage = process.memoryUsage();
console.log('Memory usage:', {
    rss: `${Math.round(memUsage.rss / 1024 / 1024)} MB`,
    heapTotal: `${Math.round(memUsage.heapTotal / 1024 / 1024)} MB`,
    heapUsed: `${Math.round(memUsage.heapUsed / 1024 / 1024)} MB`
});

// Browser memory monitoring
if ('memory' in performance) {
    console.log('Memory info:', {
        used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
        total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024),
        limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024)
    });
}

Benchmarking

// Simple benchmarking
function benchmark(fn, iterations = 1000) {
    const start = performance.now();
    for (let i = 0; i < iterations; i++) {
        fn();
    }
    const end = performance.now();
    return (end - start) / iterations;
}

// Compare two functions
const time1 = benchmark(() => arrayMethod1());
const time2 = benchmark(() => arrayMethod2());
console.log(`Method 1: ${time1.toFixed(4)}ms per iteration`);
console.log(`Method 2: ${time2.toFixed(4)}ms per iteration`);

// Benchmark with warmup
function benchmarkWithWarmup(fn, iterations = 1000, warmup = 100) {
    // Warmup phase
    for (let i = 0; i < warmup; i++) {
        fn();
    }
    
    // Actual benchmark
    return benchmark(fn, iterations);
}

// Memory benchmarking
function memoryBenchmark(fn) {
    const memBefore = process.memoryUsage().heapUsed;
    fn();
    const memAfter = process.memoryUsage().heapUsed;
    return memAfter - memBefore;
}

Performance Monitoring

// Monitor performance over time
const performanceMetrics = {
    operations: 0,
    totalTime: 0,
    maxTime: 0,
    minTime: Infinity
};

function trackPerformance(operation) {
    const start = performance.now();
    const result = operation();
    const duration = performance.now() - start;
    
    performanceMetrics.operations++;
    performanceMetrics.totalTime += duration;
    performanceMetrics.maxTime = Math.max(performanceMetrics.maxTime, duration);
    performanceMetrics.minTime = Math.min(performanceMetrics.minTime, duration);
    
    return result;
}

// Get performance statistics
function getPerformanceStats() {
    return {
        operations: performanceMetrics.operations,
        averageTime: performanceMetrics.totalTime / performanceMetrics.operations,
        maxTime: performanceMetrics.maxTime,
        minTime: performanceMetrics.minTime,
        totalTime: performanceMetrics.totalTime
    };
}

Memory Management

Memory Leaks Detection

// Common memory leak patterns
function createLeak() {
    const element = document.getElementById('myElement');
    
    // ❌ Memory leak: event listener not removed
    element.addEventListener('click', () => {
        console.log('Clicked');
    });
    
    // ✅ Proper cleanup
    const handler = () => console.log('Clicked');
    element.addEventListener('click', handler);
    
    // Store reference for cleanup
    element._clickHandler = handler;
}

// Cleanup function
function cleanup(element) {
    if (element._clickHandler) {
        element.removeEventListener('click', element._clickHandler);
        delete element._clickHandler;
    }
}

// WeakMap for avoiding memory leaks
const cache = new WeakMap();
function expensiveOperation(obj) {
    if (cache.has(obj)) {
        return cache.get(obj);
    }
    const result = computeExpensiveResult(obj);
    cache.set(obj, result);
    return result;
}

// WeakSet for tracking objects
const processedObjects = new WeakSet();
function processObject(obj) {
    if (processedObjects.has(obj)) {
        return; // Already processed
    }
    // Process object
    processedObjects.add(obj);
}

Garbage Collection

// Force garbage collection (Node.js)
if (global.gc) {
    global.gc();
}

// Monitor memory usage over time
setInterval(() => {
    const memUsage = process.memoryUsage();
    console.log('Heap used:', Math.round(memUsage.heapUsed / 1024 / 1024), 'MB');
}, 5000);

// Memory leak detection
let memorySnapshots = [];
function takeMemorySnapshot() {
    if ('memory' in performance) {
        memorySnapshots.push({
            timestamp: Date.now(),
            used: performance.memory.usedJSHeapSize,
            total: performance.memory.totalJSHeapSize
        });
        
        // Keep only last 10 snapshots
        if (memorySnapshots.length > 10) {
            memorySnapshots.shift();
        }
        
        // Check for memory growth
        if (memorySnapshots.length >= 2) {
            const growth = memorySnapshots[memorySnapshots.length - 1].used - 
                          memorySnapshots[0].used;
            if (growth > 10 * 1024 * 1024) { // 10MB growth
                console.warn('Potential memory leak detected');
            }
        }
    }
}

Memory Optimization

// Object pooling for frequently created objects
class ObjectPool {
    constructor(createFn, resetFn) {
        this.pool = [];
        this.createFn = createFn;
        this.resetFn = resetFn;
    }
    
    get() {
        return this.pool.pop() || this.createFn();
    }
    
    release(obj) {
        this.resetFn(obj);
        this.pool.push(obj);
    }
}

// Usage example
const vectorPool = new ObjectPool(
    () => ({ x: 0, y: 0 }),
    (vector) => { vector.x = 0; vector.y = 0; }
);

// Avoid creating objects in hot paths
function optimizedCalculation(x, y) {
    // ❌ Bad: creates new object each time
    return { x: x * 2, y: y * 2 };
    
    // ✅ Good: reuse object
    result.x = x * 2;
    result.y = y * 2;
    return result;
}

Code Optimization

Loop Optimization

const array = Array.from({ length: 10000 }, (_, i) => i);

// ❌ Inefficient: accessing length in each iteration
for (let i = 0; i < array.length; i++) {
    // operation
}

// ✅ Efficient: cache length
const len = array.length;
for (let i = 0; i < len; i++) {
    // operation
}

// ✅ Most efficient: for...of for arrays
for (const item of array) {
    // operation
}

// ✅ Efficient: forEach for side effects
array.forEach(item => {
    // side effect operation
});

// ✅ Efficient: map for transformations
const doubled = array.map(item => item * 2);

// ✅ Early termination
for (const item of array) {
    if (item > 100) break; // Stop when condition met
    // process item
}

Function Optimization

// ❌ Inefficient: function created in each iteration
function processItems(items) {
    return items.map(item => {
        return item * 2; // New function created each time
    });
}

// ✅ Efficient: function defined once
const double = item => item * 2;
function processItems(items) {
    return items.map(double);
}

// Memoization for expensive calculations
const memo = new Map();
function expensiveCalculation(n) {
    if (memo.has(n)) {
        return memo.get(n);
    }
    
    const result = /* expensive operation */;
    memo.set(n, result);
    return result;
}

// Debouncing for performance
function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
}

// Throttling for performance
function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

const debouncedSearch = debounce(searchFunction, 300);
const throttledScroll = throttle(handleScroll, 100);

DOM Optimization

// ❌ Inefficient: multiple DOM queries
for (let i = 0; i < 100; i++) {
    document.getElementById('element').style.color = 'red';
}

// ✅ Efficient: single DOM query
const element = document.getElementById('element');
for (let i = 0; i < 100; i++) {
    element.style.color = 'red';
}

// Document fragment for multiple DOM operations
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
    const div = document.createElement('div');
    div.textContent = `Item ${i}`;
    fragment.appendChild(div);
}
document.body.appendChild(fragment);

// Batch DOM updates
const updates = [];
function batchUpdate() {
    requestAnimationFrame(() => {
        updates.forEach(update => update());
        updates.length = 0;
    });
}

function scheduleUpdate(update) {
    updates.push(update);
    if (updates.length === 1) {
        batchUpdate();
    }
}

// Virtual scrolling for large lists
function createVirtualList(items, itemHeight, containerHeight) {
    const visibleCount = Math.ceil(containerHeight / itemHeight);
    const scrollTop = container.scrollTop;
    const startIndex = Math.floor(scrollTop / itemHeight);
    const endIndex = Math.min(startIndex + visibleCount, items.length);
    
    return items.slice(startIndex, endIndex);
}

Algorithm Optimization

// Binary search instead of linear search
function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;
    
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) return mid;
        if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

// Use Set for O(1) lookups
const uniqueItems = new Set([1, 2, 3, 4, 5]);
console.log(uniqueItems.has(3)); // O(1) instead of O(n)

// Use Map for key-value lookups
const userCache = new Map();
userCache.set('user1', { name: 'John', age: 30 });
console.log(userCache.get('user1')); // O(1) lookup

// Efficient string concatenation
// ❌ Bad: creates new string each time
let result = '';
for (let i = 0; i < 1000; i++) {
    result += 'item' + i;
}

// ✅ Good: use array join
const parts = [];
for (let i = 0; i < 1000; i++) {
    parts.push('item' + i);
}
const result = parts.join('');

Profiling Tools

Chrome DevTools

// Performance profiling
console.profile('My Operation');
// ... code to profile
console.profileEnd('My Operation');

// Memory profiling
console.profile('Memory Profile');
// ... code that might leak memory
console.profileEnd('Memory Profile');

// Timeline markers
console.timeStamp('User clicked button');

// Performance observer
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        console.log('Performance entry:', entry);
    }
});
observer.observe({ entryTypes: ['measure', 'navigation'] });

Node.js Profiling

// CPU profiling
const profiler = require('v8-profiler-next');
profiler.startProfiling('My Profile');

// ... run your code

const profile = profiler.stopProfiling();
profile.export().pipe(require('fs').createWriteStream('profile.cpuprofile'));

// Heap snapshot
const snapshot = profiler.takeSnapshot();
snapshot.export().pipe(require('fs').createWriteStream('heap.heapsnapshot'));

// Built-in profiler
const inspector = require('inspector');
const session = new inspector.Session();
session.connect();

session.post('Profiler.enable');
session.post('Profiler.start');
// ... run your code
session.post('Profiler.stop', (err, result) => {
    console.log('Profile:', result.profile);
});

Performance Monitoring in Production

// Performance monitoring service
class PerformanceMonitor {
    constructor() {
        this.metrics = [];
    }
    
    measure(name, fn) {
        const start = performance.now();
        const result = fn();
        const duration = performance.now() - start;
        
        this.metrics.push({
            name,
            duration,
            timestamp: Date.now()
        });
        
        // Send to monitoring service if duration is high
        if (duration > 100) {
            this.reportSlowOperation(name, duration);
        }
        
        return result;
    }
    
    reportSlowOperation(name, duration) {
        // Send to monitoring service
        fetch('/api/metrics', {
            method: 'POST',
            body: JSON.stringify({ name, duration, timestamp: Date.now() })
        });
    }
}

const monitor = new PerformanceMonitor();
monitor.measure('expensiveOperation', () => {
    // Your expensive operation
});

Optimization Strategies

Lazy Loading

// Lazy load modules
const loadModule = async (moduleName) => {
    const module = await import(`./modules/${moduleName}.js`);
    return module.default;
};

// Lazy load images
function lazyLoadImages() {
    const images = document.querySelectorAll('img[data-src]');
    const imageObserver = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.removeAttribute('data-src');
                imageObserver.unobserve(img);
            }
        });
    });
    
    images.forEach(img => imageObserver.observe(img));
}

// Lazy load components
const LazyComponent = React.lazy(() => import('./HeavyComponent'));

Caching Strategies

// Simple cache
const cache = new Map();
function cachedFunction(key, fn) {
    if (cache.has(key)) {
        return cache.get(key);
    }
    const result = fn();
    cache.set(key, result);
    return result;
}

// LRU Cache
class LRUCache {
    constructor(capacity) {
        this.capacity = capacity;
        this.cache = new Map();
    }
    
    get(key) {
        if (this.cache.has(key)) {
            const value = this.cache.get(key);
            this.cache.delete(key);
            this.cache.set(key, value);
            return value;
        }
        return null;
    }
    
    put(key, value) {
        if (this.cache.has(key)) {
            this.cache.delete(key);
        } else if (this.cache.size >= this.capacity) {
            const firstKey = this.cache.keys().next().value;
            this.cache.delete(firstKey);
        }
        this.cache.set(key, value);
    }
}

Web Workers

// Main thread
const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });

worker.onmessage = (event) => {
    console.log('Result from worker:', event.data);
};

// worker.js
self.onmessage = (event) => {
    const { data } = event.data;
    const result = expensiveCalculation(data);
    self.postMessage(result);
};