Suggest an editImprove this articleRefine the answer for “Measuring JS code performance”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Measurement happens on three levels: in-code instrumentation (`performance.now()`, the User Timing API, `console.time()`, and `perf_hooks` in Node.js), profiling (the Performance tab in DevTools, `node --inspect` or `node --prof`) and microbenchmarks (Benchmark.js, tinybench).** A quick timer answers "how long does this take", a profiler answers "where exactly is it slow", and a benchmark answers "which of the two implementations is faster". UI responsiveness is measured separately: the Long Tasks API catches main thread blocks longer than 50 ms, and `requestAnimationFrame` gives you FPS. Page level metrics (TTFB, LCP, CLS, INP) come from Navigation/Resource Timing and the web-vitals library. Trustworthiness comes from method: warm up the JIT, fix the input data, run many repetitions and report the median instead of the mean. ```javascript const t0 = performance.now(); heavyWork(); console.log(`Time: ${(performance.now() - t0).toFixed(2)} ms`); ``` **Key point:** a timer says "how long", a profiler says "where", a benchmark says "which is faster", and no number is worth trusting without warm-up, repetitions and a median.Shown above the full answer for quick recall.Answer (EN)Image**JS performance is measured with three different tools that answer three different questions: a timer in the code tells you how long an operation takes, a profiler tells you where the time actually goes, and a microbenchmark tells you which implementation is faster.** A separate layer is real user metrics: long tasks, FPS and Core Web Vitals, because the goal is not a "fast algorithm" but a responsive interface. ## Theory ### TL;DR - Quick timing: `performance.now()`, `performance.mark()` / `performance.measure()`, `console.time()`. In Node.js the same comes from the `node:perf_hooks` module. - Profiling: the Performance tab in DevTools gives a flame chart, FPS and long tasks; Memory gives heap snapshots; Coverage shows how much JS actually runs. - Node.js is profiled with `node --inspect` plus Chrome DevTools, or `node --prof` plus `prof-process`, or clinic.js and 0x. - Microbenchmarks are written with Benchmark.js or tinybench: they do the JIT warm-up, many iterations and the statistics for you. - UI responsiveness: the Long Tasks API catches tasks longer than 50 ms, `requestAnimationFrame` gives FPS. - Page metrics: Navigation and Resource Timing (TTFB, resource loading), Core Web Vitals (LCP, CLS, INP, TBT), Lighthouse for a one-off check. - Method matters more than the tool: warm-up, fixed data, many runs, median and quantiles rather than the mean. ### Quick example ```javascript // 1. The simplest precise timer, in milliseconds with a fractional part const t0 = performance.now(); buildIndex(items); const t1 = performance.now(); console.log(`Time: ${(t1 - t0).toFixed(2)} ms`); // 2. The same through the User Timing API: marks stay in the DevTools timeline performance.mark('A'); buildIndex(items); performance.mark('B'); performance.measure('build-index', 'A', 'B'); console.table(performance.getEntriesByName('build-index')); // 3. The shortest option for debugging console.time('op'); buildIndex(items); console.timeEnd('op'); ``` ### Quick timings right in the code This is instrumentation: you place a timer around the region you care about yourself. **Browser** ```javascript // 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 // Fast and simple console.time('op'); // ... code ... console.timeEnd('op'); ``` `performance.now()` returns high resolution monotonic time counted from the start of the page, so it does not jump when the system clock changes, unlike `Date.now()`. The advantage of `performance.mark()` over a bare timer is that the marks land in the profiler timeline, so you see your own measurement next to the browser's real events. **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: finding where it is slow A timer says "slow", but it does not say "because of what". That is what a profiler is for. **Browser DevTools** - **Performance** (or Performance Insights): record, and you get a flame chart (hot functions), FPS, layout and recalculate style, long tasks (>= 50 ms). - **Memory**: heap snapshot and allocation instrumentation, used to hunt leaks and excess garbage. - **Coverage**: how much JS and CSS actually executes, which feeds straight into TBT. **Node.js** - `node --inspect` plus Chrome DevTools, which gives a CPU profile and heap data. - `node --prof` plus `prof-process`, or **clinic.js** or **0x** for convenient flame graphs. You should read the flame chart itself, not only the summary numbers: a wide bar at the bottom is the function the program spent most time in, and that is the one to optimise first. ### Microbenchmarks: comparing two implementations When the question is "which of these two functions is faster", a bare `performance.now()` lies: the first run goes through unoptimised code until the JIT warms up. - **Benchmark.js** and **tinybench** do the warm-up, many iterations and the statistics for you. ```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 the test up (discard the first run), fix the input data, do several runs, close extra tabs and extensions. ### Interface and page responsiveness metrics A user does not feel the milliseconds of one function, a user feels freezes and jank. - **Long Tasks API**: catching main thread blocks. ```javascript new PerformanceObserver((list) => { for (const e of list.getEntries()) { console.log('Long task', e.duration.toFixed(1), 'ms'); } }).observe({ entryTypes: ['longtask'] }); ``` - **rAF and 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); ``` Page level, network and render metrics: - **Navigation and Resource Timing** (`performance.getEntriesByType('navigation' | 'resource')`), the source of TTFB and resource load times. - **Core Web Vitals** (LCP, CLS, INP, TBT), collect them with the web-vitals library on real traffic. - Lighthouse (one-off), good at catching regressions, but it does not replace field metrics from real users. ### Method: how to measure correctly 1. **State the goal**: are you after CPU, GC, layout or the network? The tool follows from that. 2. **Isolate the context**: if you measure a pure algorithm, take the network and the DOM out of the picture. 3. **Warm up the JIT**: do a few throwaway runs before the measured one. 4. **Repetitions and statistics**: median and quantiles beat the mean, because a single random stall ruins an average. 5. **Control the noise**: fix the data and the input size, limit background processes. 6. **Look at the flame** (flame chart), not only at numbers, and optimise the hot bottlenecks. 7. **Check for regressions in CI**: a small benchmark plus threshold values. What to measure in typical scenarios: | Scenario | What to measure with | | --- | --- | | Algorithms or collections | Microbenchmark (Benchmark.js) plus `performance.now()` | | UI interactivity | DevTools Performance, Long Tasks, FPS | | Render and styles | The Rendering, Layout and Styles panes in the profiler; avoid layout thrashing | | Memory and leaks | Heap snapshots, allocation timeline | | Node API | `perf_hooks`, CPU profile, auditing sync methods (replace with async or worker_threads) | ### Common mistakes - A single eyeballed run: noise and a cold JIT distort the picture. - Microbenchmarking DOM operations: the result depends on the real tree and styles, so profile those in DevTools instead of a benchmark. - Comparing two implementations on different input data or different input sizes. - Optimising without a flame chart: it is easy to fix the wrong place and gain nothing. - Taking the mean instead of the median: one garbage collector outlier makes both variants look "the same". - Using `Date.now()` for short measurements: the resolution is coarse and the clock is not monotonic. - Trusting lab Lighthouse only and never collecting field Core Web Vitals from real devices.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.