Skip to main contentSkip to user menuSkip to navigation

Latency vs Throughput

Learn the difference between latency and throughput, how to measure them, and optimization strategies for better performance.

15 min readBeginner
Not Started
Loading...

What is latency vs throughput?

Latency is how long one operation takes from start to finish. A person feels it as the time between tapping "Search" and seeing results. Throughput is how many completed operations a system produces in a period, usually requests per second (RPS). It tells you how much work the system can sustain.

They answer different questions. A service can complete thousands of slow requests at once, so it has high throughput and poor latency. A single fast worker can give one request low latency but still complete only a small number of requests per second. The design goal is to meet the user-facing latency target while keeping enough spare capacity that queues do not grow.

Time for one request

Latency

Measure the whole user path, including waiting, network hops, application work, and dependencies. Percentiles reveal slow requests that an average hides.

Completed work per second

Throughput

Count successful completions over time. Arrival rate is incoming work; sustainable throughput is the rate the system can keep completing without an ever-growing queue.

Work in flight

Concurrency

Run independent work at the same time to raise throughput. It helps only until a shared CPU, database pool, network link, or downstream service becomes the constraint.

One request has both work and waiting

Latency adds along the critical path. Throughput is set by the slowest bounded stage, not by the sum of every server's capacity.

Protect

Admission

Rate limits and bounded queues decide how much new work may enter without harming work already in progress.

Compute

Application

Handlers use CPU, memory, and a limited number of workers or connections.

Wait

Dependency

Remote calls add service time and failure risk. Their connection pools commonly become the shared constraint.

Complete

Storage

Queries, locks, cache misses, and I/O determine whether the request can finish within its deadline.

The invariant: stable queues need spare capacity

For a stable service, the arrival rate must remain below the capacity of its limiting stage: arrival rate < sustainable capacity. As utilization approaches 100%, even small bursts wait behind work already in progress. Once arrivals exceed completions, a queue grows until requests time out, are rejected, or users leave.

Little's Law gives a useful first estimate: concurrency = throughput x time in system. At 200 RPS and 0.25 seconds of end-to-end latency, expect about 50 requests in flight on average. It does not say that 50 workers are sufficient; it gives a quantity to measure and investigate.

Use an end-to-end deadline, then allocate a budget to each stage. A 300 ms API target might reserve 40 ms for network, 70 ms for application work, 120 ms for a database call, and 70 ms for queueing, retries, and margin. The budget is a contract: a faster app cannot compensate for a dependency that already uses the whole deadline.

Lab 1: protect a latency budget from queue pressure

This model has a 55 ms fixed path plus one bounded service gate. Change the incoming load, the gate's effective capacity, and the user-facing target. The queue estimate becomes steep near saturation because a request must wait for prior requests to leave the same gate.

Latency budget lab

See queueing consume the tail-latency budget

This teaching model keeps 55 ms of fixed work and estimates one constrained service gate. It is useful for reasoning, not a substitute for a load test.

Effective gate capacity

Tail-latency target

The target holds with measurable spare capacity.

The gate drains work faster than it arrives, leaving time for normal variation.

Gate utilization

53%

Below 100% is necessary, not sufficient.

Estimated mean

69 ms

55 ms fixed path plus queueing

Estimated tail

121 ms

Compared with 300 ms target

Model used

When arrivals are below capacity, estimated mean time at the constrained gate is `1 / (capacity - arrival rate)`. The displayed tail uses a 4.6x multiplier for an exponential-service approximation. Real paths have multiple queues and uneven service times, so measure them separately.

The estimate assumes a steady workload and an exponential service-time distribution. Production systems need measured service-time distributions, burst tests, and separate limits for each dependency. The invariant still holds: operating close to the limit makes tail latency fragile.

Read the right latency signal

An average can look healthy while a small but important group of users waits far too long. Record a distribution for the same user path and workload shape:

  • P50: the middle request. It describes the typical experience.
  • P95: a warning that contention, cache misses, or a slow dependency is affecting a meaningful minority.
  • P99: the tail where retries, queueing, and shared-resource exhaustion become visible.
  • Timeout and rejection rate: the work the latency graph may omit because it never completed successfully.

Pair those measurements with arrival rate, completed RPS, queue depth, connection-pool wait time, and utilization. A rise in queue depth with flat completed RPS is evidence that the service is accepting more work than its constraint can release.

Failure behavior: do not turn waiting into an outage

When a downstream dependency slows, unbounded waiting consumes the resources needed for healthy work. Design a controlled failure path:

  • Set a deadline shorter than the caller's deadline, so there is time to return a useful fallback or error.
  • Bound each queue and connection pool. Reject or shed low-priority work before memory, threads, and sockets are all consumed.
  • Use retries sparingly.
    • Retry only failures that are transient and safe to repeat.
    • Add jitter and a cap; retrying a saturated dependency multiplies its load.
  • Isolate optional work such as analytics, email, or recommendations from the checkout, payment, or write path that must remain responsive.

A longer timeout is not extra capacity. Increasing a timeout can make a dashboard show fewer immediate errors while requests wait longer and hold more resources. First identify whether the dependency can actually complete more work per second.

Lab 2: choose batching and concurrency for the workload

Batching amortizes a fixed cost such as a network round trip or disk flush across more items. It can raise throughput, but the first item waits while the batch fills and every item waits for the batch to run. Change the workload deadline, batch size, worker count, and arrival rate to see when a throughput improvement violates the latency contract.

Batch and concurrency lab

Trade per-item latency for completed work

Each batch has 12 ms of fixed overhead plus 3 ms per item. The model exposes the cost of filling and processing a batch before every item completes.

Workload deadline

This shape meets the selected deadline.

Each batch amortizes 12 ms of fixed work while keeping average fill time inside the deadline.

Batch run time

42 ms

Fixed work plus per-item work

Average fill wait

18 ms

Earlier items wait longer

Capacity

476 items/s

238 per worker

Estimated item time

60 ms

200 ms workload deadline

What this makes visible

A larger batch increases items completed per fixed overhead, but it also increases run time and fill wait. More workers raise the model's capacity only while CPU, storage, and downstream limits have headroom. In production, cap both batch size and maximum batch age.

For an interactive request, a small batch or no batch may be correct even when it costs more per item. For a background job with a minutes-long deadline, a larger batch can be an efficient choice. Pick the workload's deadline first; do not label a higher RPS number as an automatic win.

Parallel independent work

Add concurrency

Use more workers or instances when work is independent and the dependency has spare capacity. Stop when contention, lock time, or downstream saturation rises faster than completions.

Amortize fixed work

Batch deliberately

Combine compatible work when callers can wait. Put a maximum age on a batch so low traffic does not create an unlimited wait for the first item.

Protect user deadline

Move work off-path

Return after the durable user-visible action, then process email, indexing, analytics, or fan-out asynchronously with an observable queue and retry policy.

Make the trade-off explicit in architecture

Start from the user action and make one path responsible for meeting its deadline. Keep the synchronous path short: validate input, read or write the minimum durable state, and return. Put non-critical fan-out behind a durable queue with an owner, maximum queue age, and dead-letter or replay plan.

Common choices have different costs:

  • A cache can remove a dependency hop and improve both latency and throughput, but must define freshness and invalidation behavior.
  • A read replica can add read capacity, but replication lag may violate a read-after-write expectation.
  • Horizontal scaling raises application capacity, but cannot outscale a shared database, lock, partition, or third-party API.
  • Load shedding lowers accepted throughput during stress, but preserves a bounded latency and higher completion rate for the work that remains.

The next lesson, Bottleneck Analysis, shows how to prove which resource is constraining the result before changing architecture.

Operate to the boundary, not the average

Use a load test that matches the production request mix, payload size, cache state, and burst pattern. Record a baseline before a change, then compare the same workload after it.

  • Alert on sustained high utilization, queue age or depth, P95/P99 latency, rejected work, and dependency-pool wait time.
  • Define a capacity trigger before the SLO is breached, such as adding capacity at 70-80% sustained utilization rather than waiting for 100%.
  • Review a deployment or incident with the actual arrival rate, completions, latency distribution, errors, and saturation signal together.
  • Test degraded modes: a slow database, a full queue, a depleted connection pool, and a failed worker should produce bounded, understandable behavior.

Capacity is not a single server number. It is a tested promise about workload shape, latency target, error behavior, and the weakest shared resource.

No quiz questions available
Could not load questions file
Spotted an issue or have a better explanation? This page is open source.Edit on GitHub·Suggest an improvement