Back to the map

Module 04 · CPU scheduling · Lesson 1 of 5

Scheduling goals and metrics

What a CPU scheduler is trying to optimise and why no scheduler can win on every axis: throughput, turnaround, waiting time, response time and fairness. How to measure each.

IntroMust-knowJunior 9 min Updated Nov 2026

Why it matters

Whenever there are more runnable threads than cores, something decides who runs next. That decision shapes how responsive a laptop feels, how much batch work a server finishes per hour, and whether a background job can starve the request handlers. The same choices appear one level up in every queue you design: a job scheduler, a thread pool, a load balancer, a message consumer. Learning the vocabulary here pays off in all of them.

The idea

The scheduler is the part of the kernel that picks which runnable thread gets a core next and for how long. It keeps runnable threads in one or more run queues, and it runs whenever a thread blocks, a time slice ends, or a new thread becomes runnable. Its policy is a set of trade-offs between goals that conflict.

The goals

MetricDefinitionWho cares
CPU utilisationFraction of time cores do useful workAnyone paying for hardware
ThroughputJobs completed per unit timeBatch processing, analytics, CI runners
Turnaround timeFrom a job's arrival to its completionSomeone waiting for a batch job to finish
Waiting timeTime spent ready but not runningA proxy for how much the queue hurts each job
Response timeFrom arrival to the first time it runs (or first output)Interactive users, request handlers, UIs
FairnessEach thread or user gets its share over timeMulti-tenant systems; avoiding starvation
PredictabilityLow variance; deadlines metReal-time systems, audio, control loops

Why you cannot win them all

Finishing the shortest jobs first minimises average waiting time and can starve long jobs forever. Switching often keeps response time low and wastes CPU on context switches. Giving each tenant an equal share is fair and can lower total throughput when one tenant's work would run more efficiently in a long uninterrupted batch. Every scheduler, in the kernel or in your application, chooses a point in this space, and the right point depends on the workload: interactive, batch, latency-sensitive services, or a mix.

Short slicesfast responseLong sliceshigh throughputtime slice lengthmore context switchesnewcomers wait longermodern schedulers adapt per thread
The core tension: short time slices make every job start quickly but cost switches; long slices maximise throughput and make newcomers wait.

Workload shapes

Threads alternate between CPU bursts (computing) and I/O waits. Interactive and request-handling threads have short bursts and wait a lot; they want to run quickly when they wake, and they barely use their slices. Batch threads have long bursts; they want long uninterrupted slices for cache locality. A good general-purpose scheduler favours threads that have been waiting (they are probably interactive) without letting compute-heavy threads starve.

Measuring it on a real machine

The load average on Linux is the average number of threads that are runnable or in uninterruptible wait over 1, 5 and 15 minutes; a load persistently above the core count means work is queuing. Run queue length (vmstat's r column) shows how many threads are waiting for a core right now. Per-thread scheduling latency, the time from becoming runnable to running, is the direct measure of queueing and can be traced with tools like perf sched or eBPF-based run-queue latency histograms. CPU steal time on virtual machines shows the hypervisor running other guests on your cores.

The same trade-offs, one level up

A thread pool with a FIFO queue, a job scheduler deciding which build runs next, a consumer choosing which partition to poll, and a load balancer choosing which server gets a request all face the same choice between throughput, response time and fairness. Recognising the scheduling problem in them lets you borrow the solutions: separate queues for interactive and batch work, aging to prevent starvation, and weighted fair sharing per tenant.

Trade-offs

Optimising average metrics (mean waiting time) can hide terrible tail latency for some jobs; optimising worst-case fairness can lower average throughput. Short time slices buy responsiveness with overhead; long ones buy efficiency with latency. Measurement matters because intuition about which metric a system needs is often wrong.

In an interview

Name the metric before the policy: "This queue serves interactive API calls and nightly exports, so response time matters for one and throughput for the other. Put them in separate queues with weighted shares so exports cannot starve requests, and age long-waiting jobs so nothing waits forever." That is a scheduler design in one breath, applied to an application.

Check yourself

A job queue always runs the job with the shortest estimated duration next. Average completion time is excellent, but a few large jobs submitted on Monday are still waiting on Friday. What is happening, and what is the standard remedy?

Scheduling goals and metrics · Backplane