What must survive a task switch
Follow a thread across descheduling and resumption, separating architectural state, address spaces, and cache effects.
On this page
The question for today
What you’ll understand
- Distinguish a privilege transition from a scheduler context switch.
- Identify the state needed to resume a suspended thread correctly.
- Build an overhead budget without treating one benchmark as a universal switching cost.
A context switch is a transfer of execution from one schedulable task to another while preserving the ability to resume both correctly. Imagine pausing a calculation at an instruction boundary where switching is permitted, lending the CPU to another calculation, and later continuing without changing the first calculation's answer. The operating system must preserve the relevant execution state and install the next task's environment. Understanding that contract explains why a fast system call can involve no task switch, why two threads sharing an address space still need scheduling, and why switching cost is more than register copying.
The continuation that must survive
A thread's continuation includes an instruction location, stack state, register values, and the kernel bookkeeping that identifies its execution context. The exact division between an entry frame, a kernel stack, and explicitly saved switch state depends on architecture and the path into the scheduler. Do not imagine that one portable function blindly copies every register on every switch. Some values have already been saved by an interrupt or system-call entry sequence; other state is preserved according to architecture-specific conventions. Correctness means the resumed task observes its promised machine state, rather than prescribing one universal implementation of that promise.
The stack deserves special attention because suspended kernel calls may retain local variables and return addresses. Suppose a thread waits for a socket to become readable. Its execution has entered the kernel, determined that progress requires an external event, and blocked. Another task can now use the processor. When the socket event wakes the first thread, it becomes runnable; it does not necessarily resume immediately. Eventually the scheduler restores the continuation that lets its waiting operation recheck conditions and proceed. A wakeup therefore changes eligibility, while dispatch actually transfers execution. Confusing these events makes latency traces difficult to interpret.
Execution state is not the whole machine
A switch between threads in the same process can retain the same address-space mappings while changing stacks and thread-specific state. Switching between different address spaces may require installing different translation context. Translation caches complicate the picture: address-space identifiers can allow entries to coexist, while invalidations remain necessary when mappings change. It is incorrect to claim that every context switch always flushes every cache. Ordinary data caches usually contain reusable contents tagged by physical location, but the incoming workload may displace the outgoing workload's useful data. A resumed task can therefore pay indirect locality costs even when the switch path itself was short.
Floating-point and vector extensions add another layer. Their architectural state can be large, and enabled features differ across CPUs and operating-system configurations. Linux's x86 documentation discusses extended state and dynamically enabled features such as AMX; this is a concrete reason to record hardware and kernel configuration alongside measurements. Separately, entering the kernel may change page-table context for isolation even when the same thread returns immediately. That security transition is not proof that another task ran. Keep three questions separate: did privilege change, did address-space translation context change, and did the identity of the running task change?
Measure a boundary and a consequence
Define the measured boundary before reporting a cost. A ping-pong benchmark between two threads includes synchronization, scheduler decisions, handoff, and possibly cache-line transfer. Dividing total time by the number of handoffs yields a useful benchmark-specific average, not an architecture-independent constant. A production request may instead experience long runnable delay before any actual switch occurs. Record elapsed time, task CPU time, scheduling events, affinity, and competing load where possible. Compare repeated distributions under the same conditions. If a new configuration reduces handoff time but lengthens queueing, the request can still become slower despite a technically faster switching path.
A first-order capacity budget can treat the measured direct cost per switch as constant over a narrowly defined experiment. This lets you ask whether switching consumes a negligible or material fraction of one CPU-second. It cannot predict cache recovery, simultaneous multithreading interference, or long-tail dispatch delay. In the lab, compare two separate fixed-rate capacity assumptions: arrival 2 with service 3, then arrival 2 with service 1. Before each run, press Reset, re-set both desired rates, predict the direction of change from four work units, then press Play or Step. Editing a control pauses but retains elapsed time, so it does not enact an overhead change along the previous trajectory. Each fresh run changes backlog with its own fixed arrival-minus-service rate. It has no tasks, registers, run queues, or context-switch events; use it to inspect capacity sensitivity, then return to scheduler traces to test the real mechanism.
Work it through
Budgeting direct switch overhead
In a fictional single-CPU experiment, a nonoverlapping direct-switch measurement gives 2 microseconds per switch and a workload causes 20,000 switches per second. Assume that measured cost is constant and excludes cache recovery. Estimate the direct CPU-time fraction.
Convert 2 microseconds to 0.000002 seconds per switch.
Multiply 20,000 switches per second by 0.000002 seconds per switch to obtain 0.04 CPU-seconds per wall-clock second.
Express the dimensionless fraction 0.04 as 4%; the remaining 96% is only an accounting remainder, not a guaranteed application throughput.
Direct switching consumes an estimated 4% of one CPU under these assumptions. Indirect cache costs and queueing are separate measurements.
The living notebook
Explore the model
Interactive lab · queue
Usable capacity after overhead
Compare separate fixed-rate runs at λ=2, μ=3 and λ=2, μ=1. Each time, press Reset, re-set both rates, predict growth or drainage from four work units, then Play or Step. The second run has its own fresh backlog; editing μ alone retains elapsed time rather than modeling a live capacity reduction. Service is assumed usable capacity, not a context-switch or CPU scheduler simulation.
- Fluid backlog
- 4
- Net rate · λ − μ
- −1 /s
- M/M/1 mean residence
- 1 s
Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forSmall capacity reductions can change whether backlog drains. The separate M/M/1 residence-time readout requires its stochastic assumptions and arrival rate below service rate.
Pause & reason
Check your understanding
Your turn
Try it yourself
A hypothetical one-CPU trace contains 12,000 directly measured switches per second, each costing 3 microseconds under the stated measurement boundary. Estimate the direct fraction and name one missing cost.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Convert microseconds to seconds, then multiply rate by cost. Consider what the resumed application must reload.
Compare with the solution
The estimate is 12,000 × 0.000003 = 0.036, or 3.6% of one CPU. Cache or translation recovery after resumption is a possible missing indirect cost; runnable waiting time is another distinct latency component.
Keep the idea
Take it with you
- A resumable continuation is architecture-specific state, not merely a process identifier.
- Kernel entry, address-space changes, and scheduler task switches are different events.
- Separate direct handoff cost, indirect locality loss, and runnable waiting time when measuring latency.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.