skip to content

A three-step processing chain runs each step on its own worker with queues between them, taking 10 ms, 50 ms and 10 ms per item. What throughput and per-item latency do you expect, and what would you change to raise throughput?

level: middleimportance: must knowfreq 50%

answer

  1. Rate = 1/max, not 1/sum
  2. Speedup = sum/max = 1.4x here
  3. Efficiency = sum/(n·max) ≈ 47%
  4. Full queue upstream of bottleneck, empty downstream
  5. Cheaper → replicate → split → merge

basics

~20 s

Throughput is 1 per 50 ms (20/s), set by the slowest step; latency is at least 70 ms plus waiting. To improve it, attack only the 50 ms step: make it cheaper, split it into smaller sequential steps, or run several copies of it in parallel. Speeding up the 10 ms steps changes nothing.

solid answer

~60 s

Throughput = 1 / max(stage) = 1/50 ms = **20 items/s**. Latency per item ≥ 10 + 50 + 10 = **70 ms**, plus queueing in front of the bottleneck — which grows without bound if arrivals exceed 20/s and the queue is unbounded. Balance efficiency is 70 / (3 × 50) ≈ 47%: the two fast workers idle 80% of the time. Only the bottleneck matters. Options, roughly in order of preference: 1. **Make it cheaper** — cache, batch, remove per-item work. Best return, no new concurrency. 2. **Replicate it** — fan out to k copies of the slow stage and fan back in. k = 5 makes it 10 ms effective and moves the bottleneck elsewhere. Costs: item ordering is lost unless you re-sequence, and the resource behind that stage must actually scale. 3. **Split it** into sub-stages of ~25 ms each if the work decomposes — deepens the pipeline instead of widening it. 4. **Merge** the two 10 ms stages to free a worker and remove a handoff. Then re-measure: the bottleneck moves, it does not disappear.

go deeper

for a junior

Get the numbers right: 20 items/s, 70 ms latency, and the slow stage is the only one worth touching.

for a middle

Add speedup and efficiency arithmetic and list the concrete rebalancing moves — reduce, replicate, split, merge — with their tradeoffs.

for a senior

Lead with measurement (queue depths, per-stage utilization), cover the ordering consequences of replication and what happens when arrival rate exceeds the drain rate.

for a principal

Treat it as a capacity problem: which resource is actually saturated, whether the fix scales, cost per unit of throughput, and the iterative measure-fix-remeasure loop rather than a single change.

## The arithmetic In steady state a pipeline emits one item per **slowest stage** time, because every item must pass through that stage and it can serve only one at a time. So: - **Throughput** = 1 / max(10, 50, 10) ms = 20 items/s - **Latency** = 10 + 50 + 10 = 70 ms minimum, plus time spent waiting in buffers - **Speedup over sequential** = sum / max = 70/50 = 1.4x, despite using three workers - **Balance efficiency** = sum / (n · max) = 70 / 150 ≈ 47% A perfectly balanced 3-stage split of the same 70 ms of work (23.3 ms each) would give 43 items/s — over twice as much from the same three workers. That gap is the whole point of balancing: an unbalanced pipeline pays for n workers and gets sum/max of them. ## Why the fast stages don't matter Optimizing a 10 ms stage to 5 ms changes throughput not at all: the bottleneck still gates the rate, and the fast stage simply idles more. This is Amdahl-flavoured reasoning applied to stages — effort spent off the critical stage returns nothing. It also means measurement must identify *which* stage is slowest before any tuning starts. The cheapest instrument is **queue depth**: the buffer feeding the bottleneck is persistently full (or growing) while the buffers downstream of it are persistently empty. That signature localizes the bottleneck without profiling any code. ## What happens if arrivals exceed 20/s If the source pushes 30 items/s into a pipeline that drains 20/s, 10 items/s accumulate. With an **unbounded** buffer the queue and the memory footprint grow without limit and per-item latency grows linearly with time — the system looks healthy (no errors, workers busy) until it dies. With a **bounded** buffer the producer blocks when it is full, which pushes the pressure back to the source where it can be handled. Bounding turns a silent memory leak into a visible slowdown at the entrance. ## Ways to rebalance **Reduce the work.** Caching a lookup, batching the I/O the stage performs, or moving per-item work out of it is always the first move: it costs no extra worker and no ordering. **Replicate the stage (widen).** Run k copies of the slow stage pulling from the same queue and pushing to the next; effective service time becomes 50/k ms. This is the standard fix and the reason real pipelines are stages-of-pools rather than stages-of-single-workers. Caveats: (a) the underlying resource must scale — five copies of a stage that all hit one saturated database gains nothing and may lose; (b) **ordering is lost**, since copies finish out of order, so if downstream needs order you must attach sequence numbers and re-sequence, which reintroduces a buffer and head-of-line blocking; (c) if the stage has shared mutable state, replication needs synchronization that can eat the gain. **Split the stage (deepen).** If the 50 ms decomposes into two 25 ms halves with a clean handoff, you get two stages and a 25 ms cycle while keeping strict ordering with single-threaded stages. It only works if the work genuinely divides and the intermediate value is cheap to pass. **Merge cheap stages.** Two 10 ms stages that use the same resource can become one 20 ms stage: one fewer handoff, one fewer queue, one worker freed to be spent on the bottleneck. Handoff cost is real, and stages that are much cheaper than a handoff are net negative. ## The bottleneck always exists After fixing the 50 ms stage you have 10/10/10 and a 100 items/s pipeline whose bottleneck is now every stage at once. The next constraint may be the source, the sink, the queue handoff cost, or a shared resource behind several stages. Balancing is iterative, and the honest answer in an interview names the measurement loop — instrument stage service times and queue depths, fix the top one, re-measure — not a one-shot fix.

  • You replicate the 50 ms stage five ways. What breaks that did not break before?
    Ordering: five workers finish items out of order, so anything downstream that assumed input order (sequential writes, ordered event streams, per-key sequencing) now sees interleaving. Fixes are sequence numbers plus a re-sequencing buffer, or partitioning by key so each key stays on one worker. You also need the resource behind the stage to actually support five-way concurrency, otherwise you have just moved the contention.
  • How would you find the bottleneck stage in a running pipeline without a profiler?
    Watch queue depths. The buffer feeding the bottleneck stays full or grows while the buffers after it stay near empty, so the transition from full to empty points at the slow stage. Complement that with per-stage service time and utilization: the bottleneck runs near 100% busy while the others idle in proportion to (max − their own time).

saying these in an interview costs you the question

  • Computing throughput as 1 divided by the total 70 ms
  • Proposing to optimize the fast stages, or to add workers to every stage uniformly
  • Replicating the slow stage without mentioning ordering loss or whether the underlying resource scales
  • Claiming that after balancing there is no bottleneck
  • Assuming three workers means 3x regardless of stage times

context