Bird
Raised Fist0
Node.jsframework~8 mins

Forking workers per CPU core in Node.js - Performance & Optimization

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Performance: Forking workers per CPU core
MEDIUM IMPACT
This pattern affects server-side request handling speed and responsiveness by utilizing multiple CPU cores to handle concurrent tasks.
Handling multiple incoming requests efficiently on a multi-core server
Node.js
import cluster from 'cluster';
import http from 'http';
import os from 'os';

if (cluster.isPrimary) {
  const cpuCount = os.cpus().length;
  for (let i = 0; i < cpuCount; i++) {
    cluster.fork();
  }
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World');
  }).listen(8000);
}
Forking one worker per CPU core maximizes CPU usage and allows handling many requests in parallel.
📈 Performance GainImproves concurrency, reduces request queueing, and lowers INP by distributing load across cores.
Handling multiple incoming requests efficiently on a multi-core server
Node.js
import cluster from 'cluster';
import http from 'http';

if (cluster.isMaster) {
  // Fork only one worker
  cluster.fork();
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World');
  }).listen(8000);
}
Using only one worker process underutilizes CPU cores, causing slower request handling under load.
📉 Performance CostBlocks handling multiple requests concurrently, leading to higher INP and slower response times.
Performance Comparison
PatternCPU UtilizationRequest ConcurrencyResponse DelayVerdict
Single worker processLow (uses 1 core)Low (serial requests)High (requests queue)[X] Bad
One worker per CPU coreHigh (uses all cores)High (parallel requests)Low (fast responses)[OK] Good
Rendering Pipeline
While this concept is server-side, it impacts the browser's interaction responsiveness by reducing server response delays.
Request Handling
Response Time
⚠️ BottleneckSingle-threaded server process limits concurrency and increases request wait times.
Core Web Vital Affected
INP
This pattern affects server-side request handling speed and responsiveness by utilizing multiple CPU cores to handle concurrent tasks.
Optimization Tips
1Fork one worker process per CPU core to maximize concurrency.
2Avoid single worker setups on multi-core servers to prevent request delays.
3Monitor server response times to verify effective worker utilization.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main benefit of forking one worker per CPU core in Node.js?
ASimplifies code by avoiding clusters
BImproves concurrency by using all CPU cores
CReduces memory usage by sharing one process
DIncreases single-thread performance
DevTools: Network
How to check: Open DevTools Network tab, send multiple requests quickly, and observe response times and concurrency.
What to look for: Look for reduced response times and overlapping request handling indicating parallel processing.

Practice

(1/5)
1. What is the main reason to fork workers equal to the number of CPU cores in a Node.js app?
easy
A. To make the app run slower for debugging purposes
B. To use all CPU cores and improve performance by running tasks in parallel
C. To reduce memory usage by limiting the number of processes
D. To avoid using the cluster module

Solution

  1. Step 1: Understand CPU cores and parallelism

    Each CPU core can run one process at a time, so using all cores means better performance.
  2. Step 2: Role of forking workers

    Forking workers equal to CPU cores lets Node.js run multiple tasks simultaneously, improving speed.
  3. Final Answer:

    To use all CPU cores and improve performance by running tasks in parallel -> Option B
  4. Quick Check:

    Fork workers = use all cores = better performance [OK]
Hint: More workers = more CPU cores used = faster app [OK]
Common Mistakes:
  • Thinking forking reduces memory usage
  • Believing forking slows the app
  • Confusing cluster module usage
2. Which of the following is the correct way to get the number of CPU cores in Node.js for forking workers?
easy
A. const cores = require('os').cpus().length;
B. const cores = require('cluster').cpuCount;
C. const cores = require('os').cpuCount();
D. const cores = require('os').cpus.length;

Solution

  1. Step 1: Check Node.js os module usage

    The os module has a method cpus() that returns an array of CPU core info.
  2. Step 2: Correct syntax to get core count

    Using cpus().length gives the number of CPU cores available.
  3. Final Answer:

    const cores = require('os').cpus().length; -> Option A
  4. Quick Check:

    os.cpus() returns array, length gives core count [OK]
Hint: Use os.cpus().length to count CPU cores [OK]
Common Mistakes:
  • Forgetting parentheses after cpus
  • Using non-existent cpuCount method
  • Trying to get cores from cluster module
3. Given this code snippet, what will be logged when run on a machine with 4 CPU cores?
const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  const numCPUs = os.cpus().length;
  console.log(`Primary process is running`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  console.log(`Worker ${process.pid} started`);
}
medium
A. Primary process is running Worker 1234 started Worker 1235 started Worker 1236 started Worker 1237 started
B. Primary process is running Worker started Worker started Worker started Worker started
C. Primary process is running Worker 1234 started
D. SyntaxError due to missing cluster setup

Solution

  1. Step 1: Identify primary and worker behavior

    The primary logs once and forks 4 workers (one per CPU core).
  2. Step 2: Each worker logs its own process id

    Each worker logs "Worker [pid] started" with its unique process id.
  3. Final Answer:

    Primary process is running Worker 1234 started Worker 1235 started Worker 1236 started Worker 1237 started -> Option A
  4. Quick Check:

    Primary logs once, 4 workers log with pids [OK]
Hint: Primary logs once; each worker logs with unique pid [OK]
Common Mistakes:
  • Assuming workers log without pid
  • Thinking only one worker starts
  • Confusing primary and worker logs
4. What is wrong with this code snippet that tries to fork workers per CPU core?
const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  const numCPUs = os.cpus.length;
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  console.log('Worker started');
}
medium
A. cluster.fork() is not a function
B. No error; code works fine
C. Missing else block for worker process
D. os.cpus.length is undefined; should be os.cpus().length

Solution

  1. Step 1: Check os module usage

    os.cpus is a function, so os.cpus.length is undefined and causes error.
  2. Step 2: Correct usage

    Use os.cpus().length to get the number of CPU cores correctly.
  3. Final Answer:

    os.cpus.length is undefined; should be os.cpus().length -> Option D
  4. Quick Check:

    os.cpus() is function, need parentheses [OK]
Hint: Remember os.cpus() is a function, not a property [OK]
Common Mistakes:
  • Forgetting parentheses on os.cpus()
  • Assuming cluster.fork() is missing
  • Thinking else block is required for syntax
5. You want to create a Node.js server that forks one worker per CPU core and restarts any worker if it crashes. Which code snippet correctly implements this behavior?
hard
A. const cluster = require('cluster'); const http = require('http'); const os = require('os'); if (cluster.isPrimary) { const numCPUs = os.cpus().length; for (let i = 0; i < numCPUs; i++) { cluster.fork(); } } else { http.createServer((req, res) => { res.writeHead(200); res.end('Hello from worker ' + process.pid); }).listen(8000); cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died`); }); }
B. const cluster = require('cluster'); const http = require('http'); const os = require('os'); if (cluster.isWorker) { const numCPUs = os.cpus().length; for (let i = 0; i < numCPUs; i++) { cluster.fork(); } } else { http.createServer((req, res) => { res.writeHead(200); res.end('Hello from worker ' + process.pid); }).listen(8000); }
C. const cluster = require('cluster'); const http = require('http'); const os = require('os'); if (cluster.isPrimary) { const numCPUs = os.cpus().length; for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); }); } else { http.createServer((req, res) => { res.writeHead(200); res.end('Hello from worker ' + process.pid); }).listen(8000); }
D. const cluster = require('cluster'); const http = require('http'); const os = require('os'); if (cluster.isPrimary) { cluster.fork(); } else { http.createServer((req, res) => { res.writeHead(200); res.end('Hello from worker ' + process.pid); }).listen(8000); }

Solution

  1. Step 1: Fork one worker per CPU core in primary process

    In if (cluster.isPrimary), use const numCPUs = os.cpus().length; for (let i = 0; i < numCPUs; i++) cluster.fork();
  2. Step 2: Restart workers on exit event

    In primary process, cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting...`); cluster.fork(); });
  3. Step 3: Worker creates HTTP server

    Workers create the HTTP server listening on port 8000, responding with their pid.
  4. Final Answer:

    forks one worker per CPU core and restarts any worker if it crashes -> Option C
  5. Quick Check:

    Primary forks os.cpus().length + cluster.on('exit', fork()) + workers create HTTP server [OK]
Hint: Use cluster.on('exit') in primary to restart workers [OK]
Common Mistakes:
  • Using cluster.isWorker instead of cluster.isPrimary
  • Not restarting workers on exit
  • Forking workers inside worker process