Purpose

Notes on the queueing theory section of chapter 7 of Operating Systems: Principles and Practice. Queueing theory gives back-of-the-envelope answers to questions like “what happens to response time if utilization doubles” without simulating anything. The same math applies to CPUs, disks, networks, and anything else that serves requests from a queue.

Simplifying assumptions used throughout:

  • Work preserving: the system will eventually process all requests.
  • FIFO scheduling: requests are processed in the order they arrive.

Definitions

  • Server: anything that performs tasks, e.g. CPU, disk, network link.
  • Queueing delay (): total time a task spends waiting to be scheduled. In a time-slicing system the task may wait many separate times, and is the sum of those waits.
  • Tasks queued (): number of tasks in the queue.
  • Service time (): time to complete a task, assuming no waiting.
  • Response time (): time for a task to complete, including queueing and service time.
    • . Improving overall response time means reducing either the queueing time or the service time.
  • Arrival rate (): number of tasks arriving per unit time.
  • Arrival process: the distribution of the time between task arrivals.
  • Service rate (): number of tasks completed per unit time when busy. .
  • Utilization (): fraction of time the server is busy.
    • .
    • if .
    • if .
  • Throughput (): number of tasks completed per unit time.
    • .
    • if .
    • if .
  • Tasks in the system (): tasks in the system, both in service and queued.
    • on average, since utilization equals the average number of tasks in service at a single server.

All of the symbols in one picture:

flowchart LR
    A[Arrivals, rate lambda] --> Q[Queue, length Q, wait W]
    Q --> S[Server, service time S, rate mu]
    S --> D[Departures, throughput X]
    style Q fill:#fce4ec
    style S fill:#e8f5e9

Little’s Law

For any stable system, regardless of arrival distribution or scheduling policy:

The average number of tasks in the system equals throughput times average response time. If throughput is 10 tasks per second and average response time is 5 seconds, there are on average 50 tasks in the system.

The law applies to any bounded subsystem, and that flexibility is what makes it useful. Applied to just the server (excluding the queue), the “tasks inside” is the average number in service, which is , and the “time inside” is , giving .

Examples

A server processes requests sequentially. Requests arrive (and depart) at an average of 100 requests/sec, and the average request takes 5 ms of service. Applying Little’s Law to the server alone:

The server is busy 50% of the time.

A web service takes an average of 100 ms to complete a request and handles 10,000 queries per second. Applying Little’s Law to the whole system:

Response Time vs. Utilization

Operating a system at high utilization raises the risk of overload. If , the queue grows without bound and response time grows with it. Higher arrival rate and burstier arrival processes both push queue lengths up.

Best Case: Uniform Arrival

With perfectly evenly spaced arrivals:

  • : . Queues shrink until empty and stay empty.
  • : . Queues hold steady.
  • : . Queues grow indefinitely. In practice requests get dropped or the system fails.

Worst Case: Bursty Arrival

Requests often arrive in groups. The queue grows during a burst and drains between bursts, and average response time suffers even when the average arrival rate is well under the service rate. Compare:

  • System 1: request/second, second, uniform arrivals.
  • System 2: request/second, second, bursty arrivals of 10 requests every 10 seconds.

System 1 serves each request as it arrives. The queue stays empty and every request sees second:

System 2 serves each burst sequentially. The -th request in the burst waits for the requests ahead of it, finishing at time :

Same average arrival rate, 5.5x the average response time. In general, for bursts of requests with service time each, the -th request in a burst finishes at , so

Exponential Arrivals

An exponential distribution with rate has mean , variance , and probability density

It is memoryless: given that you have already waited seconds, the probability of waiting at least more is the same as it was from the start, .

Memorylessness is what makes the math tractable. With exponential interarrival and service times, the queue becomes a state machine whose states are the number of tasks in the system, with transition rate up (an arrival) and down (a departure), and no other history matters.

Assuming , the system is stable, and solving that state machine gives response time as a function of utilization and service time:

The shape of this curve is the practical takeaway. At low utilization, response time barely rises above . As approaches 1, the denominator goes to zero and response time blows up. At you pay 2x the service time, at you pay 10x. This is why operators leave headroom instead of running servers near full utilization.

The hockey stick has no safe side of the bend

Plotted against , the curve is nearly flat below , bends visibly around , and goes vertical approaching : each halving of the remaining headroom doubles response time (2x at 0.5, 10x at 0.9, 100x at 0.99). A system provisioned at 95% utilization is not “5% away from trouble” — it is already at 20x its unloaded latency, and any burst pushes it toward the asymptote.

M/M/1 Reference Results

The exponential-arrivals, exponential-service, single-server queue in Kendall notation is M/M/1 (“M” for memoryless). Writing , the state machine solves to a geometric distribution over queue states, , and everything follows:

with and as Little’s Law cross-checks. Response time is exponentially distributed with mean , so percentiles are multiples of the mean: the -th percentile is , giving p50 = , p90 = , p99 = , p99.9 = . Both the mean and every percentile inherit the pole.

Concrete example: ms, /s, so . Then ms, tasks in system, and p99 response time is ms — a p99 46x the service time, from utilization alone, before any real-world variability is added.

Service-Time Variability: M/D/1 and M/G/1

Memoryless service is a modeling convenience. The M/G/1 queue keeps Poisson arrivals but allows any service distribution, and has an exact mean-wait formula, Pollaczek-Khinchine (Larson and Odoni §4.7):

where . The utilization pole is unchanged; service variability enters as a clean multiplier on the waiting time:

  • M/D/1 (deterministic service, ): half the M/M/1 wait. At , : versus M/M/1’s 10. Fixed-size work units buy a 2x latency improvement at identical load.
  • M/M/1 (): the baseline.
  • Heavy-tailed service (): waits scale linearly in . A bimodal workload where 5% of requests take 10 s and the rest take 0.53 s (mean 1 s, ) at has — 2.5x M/M/1 and 4.5x M/D/1 with the same mean service time and same utilization.

The operational reading: variance is a first-class capacity cost. Splitting occasional huge requests into uniform chunks, capping request sizes, or isolating slow request classes in their own queue all reduce and buy latency without new hardware.

Checking the Formulas by Simulation

A single-server FIFO queue is four lines of recurrence: a request starts when it arrives or when its predecessor departs, whichever is later. This script (run in the repo venv, 2M requests per case) checks every closed form above:

import numpy as np
rng = np.random.default_rng(0)
 
def sim(rho, sv_fn, n=2_000_000):
    arr = np.cumsum(rng.exponential(1/rho, n))   # Poisson arrivals, mu = 1
    sv = sv_fn(n)
    dep, prev = np.empty(n), 0.0
    for i in range(n):
        start = arr[i] if arr[i] > prev else prev
        dep[i] = start + sv[i]
        prev = dep[i]
    R = dep - arr
    return R.mean(), np.percentile(R, 99)
 
fast = (1 - 0.05 * 10) / 0.95                    # bimodal: mean 1, cv^2 = 4.26
bimodal = lambda n: np.where(rng.random(n) < 0.05, 10.0, fast)
 
print(sim(0.9, lambda n: rng.exponential(1.0, n)))  # M/M/1
print(sim(0.9, lambda n: np.full(n, 1.0)))          # M/D/1
print(sim(0.9, bimodal))                            # M/G/1

Measured against predicted, all at , :

Modelpredicted simulated simulated p99
M/M/110.0010.1845.2 (predicted 46.1)
M/D/15.505.5523.2
M/G/1 bimodal24.6824.71120

The Pollaczek-Khinchine prediction for the bimodal case lands within 0.2%, and the p99 column makes the tail cost visible: the heavy-tailed workload’s p99 is 5x the M/D/1 queue’s at identical mean load.

Burstiness Revisited

The burst example earlier showed 5.5x mean response time at identical average . The M/G/1 lens generalizes it: variability on the arrival side plays the same role as variability on the service side. The standard approximation for a general-arrivals, general-service queue (G/G/1, Kingman’s formula) is

with the squared coefficient of variation of interarrival times. Uniform arrivals have , Poisson , and bursty traffic — retry storms, thundering herds after a cache flush, synchronized cron jobs — pushes far above 1. Mean utilization does not appear in at all, which is the formula-level statement of why “the server is only 60% busy on average” and “requests time out every hour at :00” are consistent observations. Smoothing arrivals (jittered retries, staggered schedules, admission control) attacks exactly as chunking work attacks .

What tail-focused engineering does with these facts — percentile SLOs, fan-out amplification, hedged requests — is in Tail Latency, Percentiles, and Queueing Distributions; scheduling-policy consequences are in queueing models and tail latency.