Skip to main content

How to measure JS code performance?

Quick measurements (instrumenting the code)

Browser

javascript
// A precise timer (in ms, with fractions) const t0 = performance.now(); // ... code ... const t1 = performance.now(); console.log(`Time: ${(t1 - t0).toFixed(2)} ms`);
javascript
// User Timing API: marks and measures performance.mark('A'); // ... code ... performance.mark('B'); performance.measure('my-op', 'A', 'B'); console.table(performance.getEntriesByName('my-op'));
javascript
// Quick and simple console.time('op'); // ... code ... console.timeEnd('op');

Node.js

javascript
// perf_hooks - monotonic timers const { performance, PerformanceObserver } = require('node:perf_hooks'); performance.mark('A'); // ... code ... performance.mark('B'); performance.measure('op', 'A', 'B'); new PerformanceObserver(list => console.table(list.getEntries())) .observe({ entryTypes: ['measure'] });

Profiling (to understand "where" it lags)

Browser DevTools

  • Performance (or Performance Insights): record → get a flame chart (hot functions), FPS, layout/recalc style, long tasks (≥50 ms).
  • Memory: Heap snapshot / Allocation instrumentation - looking for leaks and "garbage".
  • Coverage: how much JS/CSS is actually executed (affects TBT).

Node.js

  • node --inspect and Chrome DevTools → CPU profile, heap.
  • node --prof + prof-process or clinic.js / 0x for convenient flame graphs.

Microbenchmarks (comparing implementations)

  • Benchmark.js / tinybench - do JIT warm-up, many iterations, statistics.
javascript
import Benchmark from 'benchmark'; const suite = new Benchmark.Suite(); function a(arr){ /* variant A */ } function b(arr){ /* variant B */ } suite .add('A', () => a(data)) .add('B', () => b(data)) .on('cycle', e => console.log(String(e.target))) .on('complete', function () { console.log('Faster:', this.filter('fastest').map('name')); }) .run({ async: true });

Important: warm up the test (discard first run), fix the input data, run several passes, disable unnecessary tabs/extensions.

Measuring UI "liveliness"

  • Long Tasks API: catch main-thread blocking.
javascript
new PerformanceObserver((list) => { for (const e of list.getEntries()) { console.log('Long task', e.duration.toFixed(1), 'ms'); } }).observe({ entryTypes: ['longtask'] });
  • rAF/FPS: a simple smoothness indicator.
javascript
let last = performance.now(), frames = 0; function tick(ts){ frames++; if (ts - last >= 1000) { console.log('FPS:', frames); frames = 0; last = ts; } requestAnimationFrame(tick); } requestAnimationFrame(tick);

Network and render metrics (page-level)

  • Navigation/Resource Timing (performance.getEntriesByType('navigation'|'resource')) - TTFB, resource loading.
  • Core Web Vitals (LCP, CLS, INP/TBT) - deploy web-vitals on real traffic.
  • Lighthouse (one-off) - catches regressions, but doesn't replace field metrics.

Methodology: how to measure correctly

  1. State the goal (CPU-bound part? GC? Layout? Network?).
  2. Isolate the context: disable the network/DOM if you're measuring a pure algorithm.
  3. Warm up the JIT: do a few "throwaway" runs.
  4. Repetitions and statistics: the median/quantiles matter more than the average.
  5. Control the noise: fix the data, the input size, limit background processes.
  6. Look at the flame (flame chart), not just the numbers - optimize the "hot" bottlenecks.
  7. Check for regressions in CI (for example, a small bench + thresholds).

What to measure in typical scenarios

  • Algorithms/collections → microbench (Benchmark.js) + performance.now.
  • UI interactivity → DevTools Performance + Long Tasks + FPS.
  • Rendering/styles → the Rendering/Layout/Styles tabs in the profiler, avoid layout thrashing.
  • Memory/leaks → Heap snapshots, Allocation timeline.
  • Node APIs → perf_hooks, CPU profile, checking for sync methods (replace with async/worker_threads).

Anti-patterns

  • A single "by eye" run - noise and the JIT will distort the picture.
  • Microbenchmarking DOM operations: results depend on the real tree/styles. Profile in DevTools instead.
  • Comparing without identical input data.
  • Optimizing without a flamechart - easy to "fix the wrong spot".

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.