Why This Matters
Every time the OS switches from one process to another, it must save the entire state of the running process and load the state of the next one. This is a context switch, and it is not free. Each switch costs precious microseconds of CPU time where no useful work gets done.
Understanding context switching explains why spawning thousands of threads can actually slow your program down, why Go uses lightweight goroutines instead of OS threads, and why the event loop model in Node.js avoids the problem entirely. It is the hidden tax on multitasking.
Define Terms
Visual Model
The full process at a glance. Click Start tour to walk through each step.
A context switch saves one process state and loads another, with overhead during the transition.
Code Example
// Demonstrating context switch impact:
// Thread pool vs Event loop approach
// --- Simulating thread-per-task overhead ---
function simulateThreadPool(tasks, switchCostMs) {
let totalTime = 0;
let switches = 0;
for (let i = 0; i < tasks.length; i++) {
totalTime += tasks[i].duration; // actual work
if (i < tasks.length - 1) {
totalTime += switchCostMs; // context switch
switches++;
}
}
console.log("Tasks completed: " + tasks.length);
console.log("Context switches: " + switches);
console.log("Useful work time: " + tasks.reduce((s, t) => s + t.duration, 0) + "ms");
console.log("Switch overhead: " + (switches * switchCostMs) + "ms");
console.log("Total time: " + totalTime + "ms");
return totalTime;
}
// --- Event loop: zero context switches ---
function simulateEventLoop(tasks) {
const totalTime = tasks.reduce((sum, t) => sum + t.duration, 0);
console.log("Event loop total: " + totalTime + "ms (no switch overhead)");
return totalTime;
}
const tasks = Array.from({ length: 100 }, () => ({ duration: 1 }));
console.log("=== Thread Pool ===");
simulateThreadPool(tasks, 0.5);
console.log("\n=== Event Loop ===");
simulateEventLoop(tasks);Interactive Experiment
Try these exercises:
- Modify the number of tasks from 100 to 10,000. How much overhead does the thread pool model accumulate?
- Change the switch cost from 0.5ms to 5ms (simulating a heavier context switch). At what point does overhead exceed useful work?
- Research how many context switches your actual system performs: on Linux run
vmstat 1and watch thecscolumn. On macOS trysudo dtrace -n 'sched:::on-cpu { @[execname] = count(); }'.
Quick Quiz
Coding Challenge
Write a function called `calculateOverhead` that takes an array of processes (each with `name` and `burstTime`) and a `quantum` and `switchCost`. Simulate round-robin scheduling and return an object with `totalTime` (including switches), `usefulWork` (just burst times), `overhead` (total switch cost), and `switches` (number of context switches). A context switch occurs every time the scheduler moves from one process to a different one.
Real-World Usage
Context switching costs shape real engineering decisions:
- Thread pools limit the number of threads to reduce switching. Java's
Executors.newFixedThreadPool(n)typically usesn = number of CPU coresto minimize overhead. - Go's goroutines cost approximately 200 bytes of stack and switch in user space, enabling millions of concurrent goroutines vs thousands of OS threads.
- Node.js avoids the problem entirely for I/O-bound work by using a single-threaded event loop. No threads means no context switches for application logic.
- Database connection pools limit concurrent connections because each connection often maps to a thread. Too many connections means excessive context switching and slower overall throughput.