Node.js Performance Optimization

Optimize your Node.js applications for speed and efficiency. Learn profiling, caching, clustering, and memory management techniques.

Performance optimization in Node.js involves understanding bottlenecks and applying targeted improvements. Measure before optimizing.

Profiling

Use the built-in profiler, Chrome DevTools, or tools like clinic.js to identify performance issues before attempting fixes.

Clustering

Utilize all CPU cores with the cluster module. Each worker runs in its own process, allowing true parallelism.

Caching

Cache expensive operations in memory with Map or Redis. Implement proper cache invalidation strategies.

Memory Management

Understand V8's garbage collector. Avoid memory leaks by properly cleaning up listeners, timers, and references.

Async Optimization

Avoid blocking the event loop. Use worker threads for CPU-intensive tasks.

Code Examples

Clustering

cluster.js
const cluster = require('cluster');
const http = require('http');
const os = require('os');

if (cluster.isPrimary) {
  const numCPUs = os.cpus().length;
  console.log(`Primary ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork(); // Restart worker
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Hello from worker ${process.pid}`);
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

Memory-Efficient Caching

cache.js
class LRUCache {
  constructor(maxSize = 100) {
    this.maxSize = maxSize;
    this.cache = new Map();
  }

  get(key) {
    if (!this.cache.has(key)) return undefined;

    // Move to end (most recently used)
    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }

  set(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.maxSize) {
      // Delete oldest entry
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

// Usage
const cache = new LRUCache(1000);

async function getUserWithCache(userId) {
  const cached = cache.get(userId);
  if (cached) return cached;

  const user = await db.findUser(userId);
  cache.set(userId, user);
  return user;
}

Profiling with Node.js

profiling.js
// Start with: node --prof app.js
// Process: node --prof-process isolate-*.log > profile.txt

const { performance, PerformanceObserver } = require('perf_hooks');

// Measure function execution time
function measureAsync(fn, label) {
  return async (...args) => {
    const start = performance.now();
    const result = await fn(...args);
    const duration = performance.now() - start;
    console.log(`${label}: ${duration.toFixed(2)}ms`);
    return result;
  };
}

// Monitor memory usage
function logMemory() {
  const used = process.memoryUsage();
  console.log({
    heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
    heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
    external: `${Math.round(used.external / 1024 / 1024)}MB`
  });
}

setInterval(logMemory, 5000);

Frequently Asked Questions

How do I find memory leaks in Node.js?

Use --inspect flag and Chrome DevTools heap snapshots. Take multiple snapshots over time and compare them. Look for objects that keep growing. Common causes: event listeners not removed, closures holding references, and global caches without limits.

When should I use worker threads vs clustering?

Use clustering to utilize multiple CPU cores for handling more concurrent requests. Use worker threads for CPU-intensive operations within a single request. Clustering is for scaling, workers are for offloading computation.

How much should I cache in memory?

It depends on your server's available memory and data access patterns. Monitor memory usage and set limits. Consider using Redis for larger caches or when you need persistence across restarts.

Need Node.js Help?

Slashdev.io builds production-ready Node.js applications for businesses of all sizes.

Get in Touch