Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Заміри продуктивності JS-коду». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Заміри йдуть трьома рівнями: інструментування в коді (`performance.now()`, User Timing API, `console.time()`, а в Node.js, `perf_hooks`), профілювання (вкладка Performance у DevTools, `node --inspect` чи `node --prof`) і мікробенчмарки (Benchmark.js, tinybench).** Швидкий таймер відповідає на питання «скільки це триває», профайлер, на питання «де саме гальмує», а бенчмарк, на питання «яка з двох реалізацій швидша». Окремо міряють відгук інтерфейсу: Long Tasks API ловить блокування головного потоку довші за 50 мс, а `requestAnimationFrame` дає FPS. Сторінкові метрики (TTFB, LCP, CLS, INP) беруть з Navigation/Resource Timing і бібліотеки web-vitals. Достовірність дає методика: прогрів JIT, фіксовані вхідні дані, багато повторів і медіана замість середнього. ```javascript const t0 = performance.now(); heavyWork(); console.log(`Час: ${(performance.now() - t0).toFixed(2)} ms`); ``` **Ключове:** таймер каже «скільки», профайлер каже «де», бенчмарк каже «що швидше», і жодне число не варте довіри без прогріву, повторів і медіани.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Продуктивність JS міряють трьома різними інструментами під три різні питання: таймер у коді каже, скільки триває операція, профайлер каже, де саме витрачається час, а мікробенчмарк каже, яка з реалізацій швидша.** Окремий шар, це метрики реального користувача: довгі задачі, FPS і Core Web Vitals, бо кінцева мета не «швидкий алгоритм», а чутливий інтерфейс. ## Теорія ### TL;DR - Швидкий замір: `performance.now()`, `performance.mark()` / `performance.measure()`, `console.time()`. У Node.js те саме дає модуль `node:perf_hooks`. - Профілювання: вкладка Performance у DevTools дає flame chart, FPS і long tasks; Memory дає heap snapshot; Coverage показує, скільки JS реально виконується. - Node.js профілюють через `node --inspect` плюс Chrome DevTools, або `node --prof` плюс `prof-process`, або clinic.js та 0x. - Мікробенчмарки пишуть на Benchmark.js чи tinybench: вони роблять прогрів JIT, багато ітерацій і статистику. - Відгук UI: Long Tasks API ловить задачі довші за 50 мс, `requestAnimationFrame` дає FPS. - Сторінкові метрики: Navigation і Resource Timing (TTFB, завантаження ресурсів), Core Web Vitals (LCP, CLS, INP, TBT), Lighthouse для разової перевірки. - Методика важливіша за інструмент: прогрів, фіксовані дані, багато прогонів, медіана і квантилі, а не середнє. ### Швидкий приклад ```javascript // 1. Найпростіший точний таймер, у мілісекундах з дробовою частиною const t0 = performance.now(); buildIndex(items); const t1 = performance.now(); console.log(`Час: ${(t1 - t0).toFixed(2)} ms`); // 2. Те саме через User Timing API: позначки лишаються у таймлайні DevTools performance.mark('A'); buildIndex(items); performance.mark('B'); performance.measure('build-index', 'A', 'B'); console.table(performance.getEntriesByName('build-index')); // 3. Найкоротший варіант для налагодження console.time('op'); buildIndex(items); console.timeEnd('op'); ``` ### Швидкі заміри прямо в коді Це інструментування: ви самі ставите таймер навколо ділянки, яка вас цікавить. **Браузер** ```javascript // Точний таймер (у мс, з долями) const t0 = performance.now(); // ... код ... const t1 = performance.now(); console.log(`Час: ${(t1 - t0).toFixed(2)} ms`); ``` ```javascript // User Timing API: позначки і вимірювання performance.mark('A'); // ... код ... performance.mark('B'); performance.measure('my-op', 'A', 'B'); console.table(performance.getEntriesByName('my-op')); ``` ```javascript // Швидко і просто console.time('op'); // ... код ... console.timeEnd('op'); ``` `performance.now()` повертає монотонний час високої точності від старту сторінки, тому він не стрибає при зміні системного годинника, на відміну від `Date.now()`. Перевага `performance.mark()` над голим таймером у тому, що позначки потрапляють у таймлайн профайлера, і ви бачите свій замір поруч із реальними подіями браузера. **Node.js** ```javascript // perf_hooks, монотонні таймери const { performance, PerformanceObserver } = require('node:perf_hooks'); performance.mark('A'); // ... код ... performance.mark('B'); performance.measure('op', 'A', 'B'); new PerformanceObserver(list => console.table(list.getEntries())) .observe({ entryTypes: ['measure'] }); ``` ### Профілювання: шукаємо, де саме гальмує Таймер каже «повільно», але не каже «через що». Для цього є профайлер. **Браузер DevTools** - **Performance** (або Performance Insights): запис, і ви отримуєте flame chart (гарячі функції), FPS, layout і recalculate style, long tasks (≥ 50 мс). - **Memory**: Heap snapshot та Allocation instrumentation, шукаємо витоки і зайве сміття. - **Coverage**: скільки JS і CSS реально виконується, це прямо впливає на TBT. **Node.js** - `node --inspect` і Chrome DevTools, звідти CPU profile і heap. - `node --prof` плюс `prof-process`, або **clinic.js** чи **0x** для зручних графів полум'я. Читати треба саме flame chart, а не лише підсумкові числа: широка смуга внизу означає функцію, у якій програма провела найбільше часу, і саме її варто оптимізувати першою. ### Мікробенчмарки: порівняти дві реалізації Коли питання звучить «яка з двох функцій швидша», голий `performance.now()` бреше: перший прогін іде по неоптимізованому коду, доки JIT не прогріється. - **Benchmark.js** та **tinybench** роблять прогрів, багато ітерацій і статистику за вас. ```javascript import Benchmark from 'benchmark'; const suite = new Benchmark.Suite(); function a(arr){ /* варіант A */ } function b(arr){ /* варіант 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 }); ``` > Важливо: прогрівайте тест (відкидайте перший прогін), фіксуйте вхідні дані, робіть кілька прогонів, закривайте зайві вкладки і розширення. ### Метрики відгуку інтерфейсу і сторінки Користувач не відчуває мілісекунд окремої функції, він відчуває підвисання і смикання. - **Long Tasks API**: ловимо блокування головного потоку. ```javascript new PerformanceObserver((list) => { for (const e of list.getEntries()) { console.log('Long task', e.duration.toFixed(1), 'ms'); } }).observe({ entryTypes: ['longtask'] }); ``` - **rAF і FPS**: простий індикатор плавності. ```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); ``` Сторінкові, мережеві та рендер-метрики: - **Navigation і Resource Timing** (`performance.getEntriesByType('navigation' | 'resource')`), звідси TTFB і час завантаження ресурсів. - **Core Web Vitals** (LCP, CLS, INP, TBT), ставте бібліотеку web-vitals на реальний трафік. - Lighthouse (разово), добре ловить регресії, але не замінює польові метрики з реальних користувачів. ### Методика: як міряти коректно 1. **Сформулюйте мету**: вас цікавить CPU, GC, layout чи мережа? Від цього залежить інструмент. 2. **Ізолюйте контекст**: якщо міряєте чистий алгоритм, приберіть мережу і DOM. 3. **Прогрійте JIT**: зробіть кілька «холостих» запусків перед заміром. 4. **Повторення і статистика**: медіана і квантилі важливіші за середнє, бо одне випадкове сповільнення псує середнє. 5. **Контролюйте шум**: фіксуйте дані та розмір входу, обмежте фонові процеси. 6. **Дивіться на полум'я** (flame chart), а не лише на числа, оптимізуйте гарячі вузькі місця. 7. **Перевіряйте регресії в CI**: невеликий бенч плюс порогові значення. Що міряти в типових сценаріях: | Сценарій | Чим міряти | | --- | --- | | Алгоритм або колекції | Мікробенч (Benchmark.js) плюс `performance.now()` | | Інтерактивність UI | DevTools Performance, Long Tasks, FPS | | Рендер і стилі | Вкладки Rendering, Layout, Styles у профайлері; уникати layout thrashing | | Пам'ять і витоки | Heap snapshots, Allocation timeline | | Node API | `perf_hooks`, CPU profile, перевірка синхронних методів (замінити на async або worker_threads) | ### Типові помилки - Один прогін «на око»: шум і непрогрітий JIT спотворять картину. - Мікробенчити DOM-операції: результат залежить від реального дерева і стилів, такі речі профілюють у DevTools, а не в бенчмарку. - Порівнювати дві реалізації на різних вхідних даних або різних розмірах входу. - Оптимізувати без flame chart: легко вилікувати не те місце і не отримати жодного виграшу. - Брати середнє замість медіани: один викид від збирача сміття зробить обидва варіанти «однаковими». - Використовувати `Date.now()` для коротких замірів: роздільність груба, а час немонотонний. - Довіряти лише лабораторному Lighthouse і не збирати польові Core Web Vitals із реальних пристроїв.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.