Purpose

This note records the small set of quantities I keep reaching for when a system feels slow. It is the entry point for the performance section: the laws here are assumption-light and reusable, the heavier queueing math lives in Queueing Theory, and the tail-focused view lives in Tail Latency and Percentiles.

The four numbers

Take one service stage.

  • arrival rate: requests per second
  • service time: seconds of work per request
  • throughput: completed requests per second
  • response time: seconds from arrival to completion

If the stage is stable, then . If requests arrive faster than the stage can serve them, a queue grows until something upstream throttles, times out, or drops work.

Utilization

Utilization is the fraction of time the stage is busy. For a single server,

and the stage can stay stable only when .

This is the reason high average utilization is dangerous. As approaches 1, even small bursts stop fitting into the slack between requests, so queueing delay starts dominating service time.

Response time

Response time splits into

where is time spent waiting in the queue and is time spent being served.

This decomposition is usually enough to narrow the problem. If is large, speed up the work. If is large, reduce load, smooth bursts, add capacity, or shorten the critical section that serializes requests.

Little’s Law

The one law that needs almost no assumptions: for any stable system observed over a long window,

— average number of requests in the system equals throughput times average time each spends inside. The intuitive proof is an accounting identity: over a long interval , each of the completed requests contributes its residence time to the total request-seconds observed, and dividing total request-seconds by is exactly the time-average occupancy . No distributional or scheduling assumptions enter, which is why the law can be applied to any boundary you can draw: a whole service, one queue, a thread pool, a connection pool.

Its everyday use is solving for the third quantity from the two you can measure. A service doing req/s with requests in flight has ms whether or not anyone instrumented latency. Run the other way, it sizes concurrency: sustaining 2,000 req/s at 150 ms requires 300 concurrent requests’ worth of capacity — threads, connections, whatever the unit of in-flight work is — and a pool capped at 100 will cap throughput at req/s no matter how fast the backend is.

Bottlenecks

A bottleneck is the stage with the highest demand relative to its capacity. If stage needs seconds of a resource per request, then its maximum throughput is roughly

for one server, or for identical servers. The bottleneck law for a pipeline follows immediately: system throughput is capped by the slowest stage,

and at that ceiling the bottleneck runs at while every other stage idles in proportion. Two corollaries do most of the diagnostic work. Optimizing a non-bottleneck changes nothing measurable — the phrasing in Millsap is that work on anything but the constraint is an illusion of progress. And the bottleneck moves after you fix it: relieving the constraint promotes the runner-up, so capacity planning is an iterative loop, not one calculation.

The saturation curve

The queueing model sharpens “high utilization is dangerous” into a shape. For exponential arrivals and service (M/M/1; derivation in Queueing Theory):

0.50.80.90.950.99
251020100

The pole at separates two regimes worth naming precisely. Stable (): queues form and drain, response time is finite, and the table above prices the headroom. Unstable (): the queue grows without bound, is no longer a function of load but of time — every second of overload adds to a backlog that must be repaid — and throughput pins at while arrivals keep accumulating. A system past saturation does not degrade gracefully; it falls behind linearly. Simulation confirming the table, plus the variance and burstiness effects the M/M/1 model hides, are in Queueing Theory.

Past saturation, latency is a function of time, not load

The curve only describes the stable regime. At there is no steady state to plot: every second of overload adds requests to a backlog that must be repaid before latency recovers. The dashboard signature is throughput pinned flat at capacity while latency climbs linearly with wall-clock time — and stays high after load drops, until the backlog drains.

The curve also explains the knee heuristic: below -0.8, added load costs little latency; above it, each point of utilization costs more than the last. Operators leave headroom not from superstition but because the marginal price of utilization is convex.

Finite buffers and dropped work

Real queues are bounded. A finite buffer converts unbounded delay into loss: when the queue is full, arrivals are dropped (or rejected, or timed out upstream). This is a genuine trade, not a defect — a bounded queue caps the worst-case a request can experience at (buffer length) x , at the price of serving less than the offered load. Under sustained overload, a bounded queue is strictly better than an unbounded one, because an unbounded queue eventually holds only requests whose clients have already timed out: the server does full-price work to produce answers nobody is waiting for, which is the mechanism behind congestion collapse. Choosing the bound is choosing the worst latency you are willing to serve; admission control and load shedding are this idea applied deliberately.

When the averages lie

Everything above is stated in means, and means hide two things. First, the distribution of : at near 1 the tail grows faster than the mean (for M/M/1 the p99 is 4.6x the mean, and both scale as ), so an SLO on p99 saturates far earlier than a mean-based capacity plan predicts. Second, mixtures: a mean over two request classes, or over bursty and quiet periods, describes no real request — and burstiness raises queueing pain at identical average (worked example in Queueing Theory). The rule of thumb: plan capacity with the formulas here, but state objectives and read dashboards in percentiles — the full argument is in Tail Latency, Percentiles, and Queueing Distributions.

Worked example

A request touches an API server ( ms CPU) and a database ( ms). Offered load 100 req/s. Demand: API , DB — the DB is the bottleneck and caps throughput at 125 req/s. Expected in-flight count by Little’s Law with ms: requests. If measured is 40, not 4, then measured must be ~400 ms — and the model says DB queueing is the only term that can be that large, before any profiler runs. Doubling DB capacity () drops its to 0.4 and its queueing to ms; the API at was never worth touching.

Sources