Skip to main content

CPU-bound та I/O-bound задачі

CPU-bound задача обмежена швидкістю центрального процесора, а I/O-bound задача обмежена часом очікування відповіді від зовнішнього ресурсу. Це не про те, скільки коду написано, а про те, що саме є вузьким місцем: обчислення чи очікування. У JavaScript ця різниця критична, бо виконання коду однопотокове.

Теорія

TL;DR

  • CPU-bound: швидкість обмежує процесор. Приклади: великі цикли, криптографія, сортування, шифрування, стиснення.
  • I/O-bound: швидкість обмежує ввід/вивід (I/O, Input/Output). Процесор простоює, поки чекає на відповідь. Приклади: запити до БД, HTTP, читання та запис файлів, мережеві виклики.
  • CPU-bound код використовує процесор на 100%, I/O-bound майже не використовує його.
  • JavaScript однопотоковий, тому CPU-bound операція блокує event loop: UI не оновлюється, події не обробляються, сервер не відповідає на інші запити.
  • Лікування CPU-bound: Web Workers, Worker Threads, child_process, розбиття на чанки, WebAssembly або нативні модулі.
  • Лікування I/O-bound: асинхронний код, кешування, пул з'єднань, черги.

Швидкий приклад

javascript
import fs from 'fs/promises'; // CPU-bound: потік зайнятий обчисленням, event loop стоїть console.time('cpu'); let total = 0; for (let i = 0; i < 1e9; i += 1) total += i; console.timeEnd('cpu'); // I/O-bound: потік вільний, поки операційна система читає файл console.time('io'); const data = await fs.readFile('large-file.txt', 'utf-8'); console.timeEnd('io');

Що саме обмежує швидкість

Тип задачіЩо обмежує швидкістьПриклади
CPU-boundЦентральний процесор (CPU), усе впирається в обчисленняВеликі цикли, криптографія, сортування, шифрування, стиснення
I/O-boundВвід/вивід (I/O, Input/Output), процесор простоює, поки чекає на відповідь від зовнішнього ресурсуЗапити до БД, HTTP, читання та запис файлів, мережеві виклики

CPU-bound, задачі, обмежені процесором

Проблема тут, довгі обчислення, які займають потік цілком.

javascript
// Важка CPU-задача: обчислення факторіала function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } console.time('CPU'); console.log(factorial(50000)); // зависне, стек переповниться console.timeEnd('CPU');

Чому це погано в JS (Node.js або браузері): JavaScript однопотоковий, і поки виконується важка операція:

  • UI не оновлюється,
  • події не обробляються,
  • сервер не відповідає на інші запити.

Рішення:

  • використати Web Workers (у браузері) або Worker Threads / child_process (у Node.js);
  • розбити обчислення на чанки (setTimeout, setImmediate);
  • використати WebAssembly або нативні модулі на Rust / C++ для важких розрахунків.

I/O-bound, задачі, обмежені вводом-виводом

Проблема тут не в процесорі, а в часі очікування відгуку зовнішньої системи.

javascript
import fs from 'fs/promises'; console.time('IO'); const data = await fs.readFile('largeFile.txt', 'utf-8'); // чекаємо на диск console.timeEnd('IO'); console.log(data.slice(0, 100));

Чому це добре для Node.js: Node.js має event loop і асинхронний I/O, тому поки файл читається або база відповідає, потік вільний і може обслуговувати інші запити.

Порівняння та аналогія

ПараметрCPU-boundI/O-bound
Затримка спричиненаОбчисленнямиОчікуванням зовнішнього ресурсу
Використовує CPUНа 100%Майже не використовує
ПрикладиСтиснення, шифрування, обробка зображень, MLЗапити до БД, API, читання файлів, мережеві операції
Підходить для Node.jsПогано, якщо без воркерівВідмінно, завдяки async/await та event loop
Як пришвидшитиБагатопотоковість, Web Workers, нативні модуліАсинхронний код, кешування, пул з'єднань

Проста аналогія:

  • CPU-bound: ви самі готуєте складну страву і не можете відволіктися, тобто завантажені на 100%.
  • I/O-bound: ви замовили доставку їжі і просто чекаєте, тобто можете зайнятися іншими справами.

Як це впливає на JS і Node.js

СценарійЩо відбувається
CPU-bound кодБлокує event loop, усі інші операції «висять»
I/O-bound кодNode.js ефективно обробляє десятки тисяч запитів паралельно

Підсумок:

Тип задачіПрикладиОптимізація
CPU-boundобчислення, сортування, криптографія, рендерингWorkers, нативний код, розбиття на частини
I/O-boundHTTP, БД, файли, мережаasync/await, кешування, черги

Типові помилки

  • Вважати, що async/await прискорює обчислення. Асинхронність не додає потоків: важкий цикл, обгорнутий у async, блокує event loop так само.
  • Масштабувати CPU-bound сервіс кількістю одночасних запитів. Поки процесор зайнятий одним обчисленням, решта запитів просто стоїть у черзі.
  • Виносити у Worker Threads тривіальну роботу. Створення воркера і серіалізація даних коштують дорожче за саме обчислення.
  • Плутати «повільно» з «CPU-bound». Повільний запит до БД, це I/O-bound, і воркери його не пришвидшать: тут допоможуть індекси, кеш і пул з'єднань.
  • Розбивати обчислення на чанки через setTimeout(fn, 0) у Node.js, коли доречніший setImmediate, і навпаки в браузері забувати про requestIdleCallback.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.