Read alongside
Scalability! But at what COST?
Why baselines must be strong, and how to report scalability honestly.
Objectives
- Apply Little’s law, the utilization law and basic queueing results to predict throughput and latency, including the latency knee near saturation.
- Bound performance with simple resource models (CPU, memory bandwidth, network, disk, or roofline for compute versus bandwidth) and decide which bound applies.
- Explain how fan-out amplifies tail latency and how hedged requests or finer partitioning change it.
- Use profilers, flame graphs and distributed traces to attribute time, distinguishing on-CPU from off-CPU time and finding the critical path.
- Design micro- and macrobenchmarks with appropriate load models, representative workloads, warm-up, repetitions and reported variability.
- Critique a performance evaluation for baseline strength, workload realism and measurement errors such as coordinated omission.
Why it matters
A claim such as "our system is three times faster" invites three questions: compared with what, under which workload, and why. A performance model answers the "why". It predicts how throughput and latency should change with load or configuration, so a measurement either confirms your understanding or exposes a surprise worth investigating. Interviewers for systems research, cloud research and performance-sensitive engineering roles look for this habit of predicting before measuring and explaining afterwards.
Core ideas
Operational laws. Little's law, L = λW, relates the average number of requests in a system to throughput and time in the system, for any stable system. The utilization law, U = X·S, says utilization equals throughput times service demand, so the resource with the largest demand per request bounds maximum throughput. Neither needs distributional assumptions, which makes them the fastest sanity check on any measurement.
Queueing intuition. With variable arrivals and service times, waiting grows nonlinearly as utilization approaches 1; in the simplest single-server model the mean response time is S/(1 − ρ). More variability, such as bursty arrivals or heavy-tailed service times, moves the knee to lower utilization. That is why "run the servers at 95% utilization" usually means unacceptable tail latency.
Bounds and rooflines. Before profiling, compute what the hardware allows: bytes moved divided by bandwidth, operations divided by peak rate. The roofline model plots attainable throughput as the minimum of peak compute and arithmetic intensity times memory bandwidth, telling you whether a kernel is compute-bound or memory-bound. The same reasoning explains why LLM decoding is limited by memory bandwidth and why FlashAttention targets memory traffic.
Tails and fan-out. If a request fans out to N servers and waits for all of them, the chance that at least one is slow grows quickly with N: with a 1% chance per server, a fan-out of 100 is slow more often than not. Hedged or tied requests, finer partitioning and reducing variability at the source attack this. Always report percentiles together with the measurement window and the number of samples.
Attribution. Sampling profilers and flame graphs show where CPU time goes; off-CPU analysis shows where threads wait on locks, I/O or scheduling. In multi-component systems, distributed traces give per-request spans, and the critical path, not the sum of all spans, determines latency. Causal profiling asks how much end-to-end performance would improve if a piece of code were faster, which is the question that actually matters.
Benchmark design. Microbenchmarks isolate one mechanism (a lock, an RPC, a kernel) and are easy to make unrealistic with warm caches, tiny data and no contention. Macrobenchmarks run the whole system on a workload and are realistic but confounded. Closed-loop generators wait for each response before sending the next request, so a slow system receives less load and its latency looks better; open-loop generators issue requests on a schedule, which matches many real services. Measuring latency from the intended send time avoids coordinated omission. Workloads should match the target's request mix, size distribution, key skew and burstiness, and baselines should be strong: a competent single-threaded implementation sometimes beats a distributed system on the same task.
Common misconceptions
- "Average latency summarizes performance." Users and fanned-out requests experience the tail; report percentiles with sample counts.
- "Low CPU utilization means the CPU path is not the bottleneck." Threads may be waiting on locks, I/O or remote calls; find which resource is saturated.
- "A closed-loop benchmark at maximum throughput shows latency under load." Closed loops throttle themselves and hide queueing; use open-loop load at controlled rates for latency curves.
- "Optimizing the widest frame in the flame graph will speed up requests." Only work on the critical path improves latency; overlapped work may not.
- "A speedup over a slow baseline shows the design is good." Compare against strong, tuned baselines, including simple single-machine implementations where relevant.
How to approach it
For every measurement in labs or projects, write the prediction first using operational laws and hardware bounds. Profile one real program with a CPU flame graph and an off-CPU view, then build a small open-loop load test and plot latency percentiles against offered load to find the knee. In interviews, start from a model ("this should be bounded by disk bandwidth at roughly ...") before naming tools.
Self-check before you finish
- What does Little’s law let you infer from throughput and concurrency alone?
- Why does latency rise steeply before utilization reaches 100%?
- When is a kernel memory-bound according to the roofline model?
- How does a closed-loop load generator hide queueing delay?
- What is coordinated omission, and how do you avoid it?
- Why can optimizing the hottest function leave request latency unchanged?