Skip to main content

CPU-bound and I/O-bound tasks

A CPU-bound task is limited by the speed of the central processor, while an I/O-bound task is limited by the time spent waiting for an external resource to respond. It is not about how much code you wrote, but about what the bottleneck actually is: computation or waiting. In JavaScript this distinction is critical, because code execution is single-threaded.

Theory

TL;DR

  • CPU-bound: the processor limits the speed. Examples: large loops, cryptography, sorting, encryption, compression.
  • I/O-bound: input/output (I/O) limits the speed. The processor idles while waiting for a response. Examples: database queries, HTTP, file reads and writes, network calls.
  • CPU-bound code uses the processor at 100%, I/O-bound code barely uses it at all.
  • JavaScript is single-threaded, so a CPU-bound operation blocks the event loop: the UI does not update, events are not handled, the server does not answer other requests.
  • Cure for CPU-bound: Web Workers, Worker Threads, child_process, splitting work into chunks, WebAssembly or native modules.
  • Cure for I/O-bound: asynchronous code, caching, connection pools, queues.

Quick example

javascript
import fs from 'fs/promises'; // CPU-bound: the thread is busy computing, the event loop is stalled console.time('cpu'); let total = 0; for (let i = 0; i < 1e9; i += 1) total += i; console.timeEnd('cpu'); // I/O-bound: the thread is free while the OS reads the file console.time('io'); const data = await fs.readFile('large-file.txt', 'utf-8'); console.timeEnd('io');

What actually limits the speed

Task typeWhat limits the speedExamples
CPU-boundThe central processor (CPU), everything comes down to computationLarge loops, cryptography, sorting, encryption, compression
I/O-boundInput/output (I/O), the processor idles while waiting for a response from an external resourceDatabase queries, HTTP, file reads and writes, network calls

CPU-bound, tasks limited by the processor

The problem here is long computations that occupy the thread entirely.

javascript
// A heavy CPU task: computing a factorial function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } console.time('CPU'); console.log(factorial(50000)); // it will hang, the stack overflows console.timeEnd('CPU');

Why this is bad in JS (Node.js or the browser): JavaScript is single-threaded, and while a heavy operation runs:

  • the UI does not update,
  • events are not handled,
  • the server does not answer other requests.

Solutions:

  • use Web Workers (in the browser) or Worker Threads / child_process (in Node.js);
  • split the computation into chunks (setTimeout, setImmediate);
  • use WebAssembly or native Rust / C++ modules for heavy calculations.

I/O-bound, tasks limited by input and output

The problem here is not the processor, but the waiting time for an external system to respond.

javascript
import fs from 'fs/promises'; console.time('IO'); const data = await fs.readFile('largeFile.txt', 'utf-8'); // waiting on the disk console.timeEnd('IO'); console.log(data.slice(0, 100));

Why this is good for Node.js: Node.js has an event loop and asynchronous I/O, so while the file is being read or the database is answering, the thread is free and can serve other requests.

Comparison and an analogy

ParameterCPU-boundI/O-bound
Delay caused byComputationWaiting for an external resource
Uses the CPUAt 100%Almost not at all
ExamplesCompression, encryption, image processing, MLDatabase queries, APIs, file reads, network operations
Fits Node.jsPoorly, unless workers are usedExcellently, thanks to async/await and the event loop
How to speed it upMultithreading, Web Workers, native modulesAsynchronous code, caching, connection pools

A simple analogy:

  • CPU-bound: you are cooking a complex dish yourself and cannot step away, so you are 100% busy.
  • I/O-bound: you ordered food delivery and are simply waiting, so you can do other things.

How this affects JS and Node.js

ScenarioWhat happens
CPU-bound codeBlocks the event loop, every other operation «hangs»
I/O-bound codeNode.js efficiently handles tens of thousands of requests in parallel

Summary:

Task typeExamplesOptimisation
CPU-boundcomputation, sorting, cryptography, renderingWorkers, native code, splitting into parts
I/O-boundHTTP, databases, files, networkasync/await, caching, queues

Common mistakes

  • Believing that async/await speeds up computation. Asynchrony adds no threads: a heavy loop wrapped in async blocks the event loop just the same.
  • Scaling a CPU-bound service by the number of concurrent requests. While the processor is busy with one computation, the rest of the requests simply queue up.
  • Moving trivial work into Worker Threads. Creating a worker and serialising the data costs more than the computation itself.
  • Confusing «slow» with «CPU-bound». A slow database query is I/O-bound, and workers will not speed it up: indexes, caching and a connection pool will.
  • Splitting computation into chunks with setTimeout(fn, 0) in Node.js where setImmediate fits better, and, in the browser, forgetting about requestIdleCallback.

Short Answer

Interview ready
Premium

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