Skip to content

Optimization

Profiling tells you where the problem is. Optimization tells you how to fix it. But optimization without understanding the underlying systems is a recipe for making things worse — a change that speeds up one benchmark may catastrophically degrade another. This section gives you deep knowledge of the systems your code runs on (V8, libuv, the OS scheduler) so that your optimizations are principled rather than lucky.

The Optimization Hierarchy

Not all optimizations are created equal. They fall into a hierarchy of impact:

LevelCategoryTypical ImpactExample
1Architecture10-1000xSwitching from polling to event-driven
2Algorithm10-100xO(n^2) to O(n log n) sort
3Data structure2-10xArray to hash map for lookups
4I/O optimization2-10xBatch queries, connection pooling
5Concurrency2-8xParallel processing, async I/O
6Caching2-100xAvoid recomputation entirely
7Memory management1.5-3xReduce GC pressure, avoid allocations
8Engine-level1.1-2xV8-friendly code patterns
9Micro-optimization1.01-1.1xBit manipulation, loop unrolling

Always start from the top. An architectural change that eliminates 90% of work dwarfs any amount of micro-optimization.

The Optimization Process

Rules:

  1. Never optimize without a target. "Make it faster" is not a target. "Reduce P99 latency from 800ms to 200ms" is.
  2. Change one thing at a time. Otherwise you cannot attribute improvements.
  3. Measure in the same conditions. Same data, same load, same hardware.
  4. Document what you did and why. Future you (or your successor) needs to understand the trade-off.

Subsections

  • Node.js Event Loop Deep Dive — libuv architecture, event loop phases, microtasks vs macrotasks, event loop lag monitoring
  • Memory Management — V8 heap layout, garbage collection algorithms, leak patterns, WeakRef and FinalizationRegistry
  • V8 Optimization — Hidden classes, inline caches, TurboFan, deoptimization, writing V8-friendly code
  • Algorithmic Optimization — Big-O analysis, amortized analysis, space-time trade-offs, practical patterns
  • Concurrency Patterns — Promise combinators, concurrency limiting, semaphores, connection pools, work queues
  • Worker Threads — Workers vs child processes vs cluster, SharedArrayBuffer, Atomics, when to use workers

Quick Reference: Common Bottlenecks and Solutions

BottleneckSignalSolutionSection
Event loop blockedHigh event loop lag, low throughputMove CPU work to workers, chunk processingEvent Loop
GC pausesLatency spikes, saw-tooth memoryReduce allocations, tune GCMemory
V8 deoptimizationSudden slowdowns, megamorphic call sitesMonomorphic code, avoid argumentsV8
O(n^2) algorithmLatency grows quadratically with dataBetter algorithm or data structureAlgorithmic
Sequential async operationsHigh latency, low throughputPromise.all, concurrency limitingConcurrency
CPU-bound on single coreOne core at 100%, others idleWorker threadsWorkers

"Make it work, make it right, make it fast — in that order." — Kent Beck

"What I cannot create, I do not understand." — Richard Feynman