Skip to main content

Latency, Throughput, Percentiles & Little's Law

Level 1: Level 1: Foundations & Mental Modelsmedium30 minlatencypercentileslittles-law

Averages hide the tail: target p99, understand how fan-out amplifies it, and size concurrency with Little's Law (L = arrival rate x latency).

Three numbers, one law, and a lying average

Three numbers describe how a system behaves under load, and confusing them is the fastest way to sound junior. Latency is how long one request takes. Throughput is how many requests complete per second (QPS). Concurrency is how many requests are in flight at once. They are not independent, and the glue between them is Little's Law.

Check yourself
100 requests hit your service: 99 finish in 10 ms and one takes 2000 ms. Your dashboard shows only the average latency. What does it report, and what should you conclude?

Averages lie

Imagine 100 requests, 99 at 10ms and one at 2000ms. The mean is 30ms, which sounds healthy, but 1% of your users just waited two seconds. The number that matters is a percentile: p50 (median) is the typical request, p99 is the request that only 1 in 100 users beats, and p99.9 is the tail your biggest, most active users hit constantly. Report p50, p95, p99, and p99.9, and treat p99 as the user number, because a heavy user who makes 100 requests per page load will almost certainly hit your p99 on every single page.

Percentiles are not evenly spaced. The gap from p50 to p90 is usually small, and then the curve turns almost vertical:

Table
The tail is not a slightly worse average, it is a different order of magnitude. The mean row is the point of the lesson: it sits between p50 and p99 and describes neither.
ReadingLatencyMultiple of medianWho feels it
p50 (median)10 ms1xThe typical request. This is what makes a dashboard look healthy.
p9015 ms1.5xStill flat. Nine in ten users cannot tell this from the median.
p99200 ms20xThe cliff. A user making 100 requests per page hits this on nearly every page load.
p99.92 s200xRare per request, constant for your largest and most active accounts.
mean30 ms3xNobody. No real request took 30 ms.
The tail is not a slightly worse average, it is a different order of magnitude. The mean row is the point of the lesson: it sits between p50 and p99 and describes neither.

That shape is why an SLO is written against a percentile and a threshold together ("p99 under 300 ms") rather than against an average. An average can improve while the tail gets worse.

Fan-out makes the tail common

Tail latency gets worse, not better, as you scale, because of fan-out. If one API request fans out to 20 backend calls and you must wait for all of them, your response is as slow as the slowest of the 20. Even if each backend has a clean 1% chance of a slow (p99) response, the probability that at least one of 20 is slow is 1 minus 0.99^20, roughly 18%. So a backend p99 becomes a frontend p82. Fan-out turns rare tails into common ones, which is why Google's "tail at scale" work pushes techniques like hedged requests (send a duplicate after the p95 mark, take the first to answer).

Little's Law

L = arrival_rate x latency, where L is the average number of requests concurrently in the system. If you serve 2000 QPS and each request takes 50ms (0.05s), then on average 2000 x 0.05 = 100 requests are in flight, so you need at least 100 units of concurrency (threads, connections, or async slots). Turn it around: if you have a fixed pool of 200 workers and latency creeps to 200ms, your ceiling is 200 / 0.2 = 1000 QPS, no matter how much traffic arrives. Little's Law is how you size pools and how you spot that rising latency is silently capping throughput.

Interview nuance: When asked "how many threads/connections do you need," reach for Little's Law out loud. concurrency = QPS x latency is a one-line answer that signals you can size a system rather than guess.

The measurement trap: coordinated omission

Many load testers send the next request only after the previous one returns. When the server stalls, the tester stalls with it and simply fails to send the requests that would have piled up, so those never get timed. The result badly understates the tail. Fix it by measuring against intended send time (record when a request should have started), or use tools like wrk2 or HdrHistogram that correct for it. Always aggregate with histograms, not by averaging per-node p99s, because you cannot average percentiles.

Recap: Averages hide the tail, p99 is the number users feel and fan-out makes it common, and Little's Law (L = arrival_rate x latency) sizes your concurrency and exposes when latency is capping throughput.

Check yourself
You run a fixed pool of 200 workers and normally serve 2000 QPS at 50 ms per request. A slow dependency pushes latency to 200 ms while arrivals stay at 2000 QPS. What happens?

Apply

Your turn

The task this lesson builds to.

Define the SLIs/SLOs for an API endpoint and explain why you target p99 latency rather than the mean, using Little's Law to relate concurrency, throughput, and latency.

Think about

  1. Why does tail latency dominate when one request fans out to many services?
  2. How does Little's Law (L = arrival rate x latency) bound concurrency?
  3. What is coordinated omission and why does it distort measured latency?

Practice

Make it stick

A second problem on the same idea, so it survives past today.

Design the latency SLOs and capacity model for Amazon's product-page assembly service during a Prime Day peak, where a single page renders by fanning out to about 100 backend services (recommendations, pricing, reviews, inventory, ads) and must return in 300ms at p99. Quantify why the tail dominates and how you size for it.

Think about

  1. At fan-out 100, what fraction of pages hit at least one backend's p99?
  2. Which techniques collapse the tail: hedged requests, per-backend deadlines, degradation?
  3. What utilization headroom does the latency-vs-utilization curve force you to keep?