34. Minimum GPU Workers to Cover Peak Load
Problem
Capacity planning for a real-time voice pipeline needs to answer a concrete provisioning question: given a batch of session intervals and the number of concurrent sessions a single GPU worker can serve, how many workers must be running to guarantee every session gets a worker at every point in time?
This reduces to finding the peak number of concurrent sessions, then dividing by each worker's capacity, rounding up — a partially-full worker still needs to exist.
Source: src/34_min_gpu_workers_for_peak.py
def min_gpu_workers(sessions: list[tuple[float, float]], capacity: int) -> int: ...
>>> min_gpu_workers([(0, 5), (1, 3), (4, 7)], capacity=1)
2
>>> min_gpu_workers([(0, 10), (0, 10), (0, 10), (0, 10), (0, 10)], capacity=2)
3
Step-by-Step Approach
- Recognize that the number of workers is entirely determined by the single worst moment in time — the peak concurrent session count — not by the total number of sessions or their total duration.
- Find that peak using the same sweep-line technique as counting peak concurrency directly: turn each session into a +1 event at its start and a -1 event at its end, sort all events by
(time, delta)so closes are processed before opens at an identical timestamp, then sweep and track the running maximum. - Once the peak concurrent count is known, each GPU worker can absorb up to
capacityof those simultaneous sessions. - The minimum number of workers needed is therefore
ceil(peak / capacity)— rounding up matters because a worker serving even one session still needs to fully exist. - Handle the empty-input edge case explicitly: zero sessions means zero peak concurrency, which needs zero workers (not
ceil(0/capacity) = 0by coincidence of integer math, but worth confirming rather than assuming). - Compute the ceiling division without importing
math.ceil: negate, floor-divide, negate back (-(-peak // capacity)) — a common integer-ceiling-division idiom that works because Python's//always floors toward negative infinity.
The key insight is that this is really two smaller, separately-recognizable problems chained together: a sweep-line peak-concurrency computation (see the peak-concurrent-sessions problem in this set) feeding into a simple capacity-division formula — you don't need to simulate assigning individual sessions to individual workers at all.
Reference solution
def _peak_concurrency(sessions: list[tuple[float, float]]) -> int:
# same sweep-line technique as "peak concurrent sessions": +1 at each
# start, -1 at each end, sorted so a close at time t is processed
# before an open at the same t (half-open interval semantics)
events: list[tuple[float, int]] = []
for start, end in sessions:
events.append((start, 1))
events.append((end, -1))
events.sort(key=lambda e: (e[0], e[1]))
concurrent = 0
peak = 0
for _, delta in events:
concurrent += delta
peak = max(peak, concurrent)
return peak
def min_gpu_workers(sessions: list[tuple[float, float]], capacity: int) -> int:
peak = _peak_concurrency(sessions)
if peak == 0:
return 0
# ceil(peak / capacity) without importing math.ceil: negate, floor-divide, negate back
return -(-peak // capacity)
Key Functions & Tricks
- Sweep-line event encoding — identical to the peak-concurrency sweep, reused here as a building block rather than re-derived.
-(-peak // capacity)— the negate/floor-divide/negate idiom for integer ceiling division, avoiding amathimport and floating-point rounding.- Explicit
if peak == 0: return 0guard — makes the empty-input behavior deliberate rather than an accident of how the ceiling-division idiom happens to behave at zero. - Splitting the solution into a private helper (
_peak_concurrency) plus a thin public function — keeps the capacity-planning formula separate from and easy to unit-test against the concurrency computation itself. - O(n log n), dominated by the event sort; the capacity division afterward is O(1).
How to Recognize This Pattern
The signal is "minimum resources to guarantee coverage of the worst-case load" — whenever a provisioning question reduces to "find the peak of some demand curve, then divide by per-unit capacity," it decomposes into a peak-finding subproblem (often the same sweep line used for pure concurrency counting) plus a one-line ceiling-division formula. A common variation replaces the flat per-worker capacity with heterogeneous worker types (different GPUs with different capacities), which turns the division into a bin-packing or greedy-assignment problem instead of simple arithmetic. Another common variation asks for an actual session-to-worker assignment, not just the count, which needs a min-heap of worker end-times (interval partitioning) rather than just the peak number. A common pitfall is forgetting the ceiling: using integer floor division under-provisions by exactly one worker whenever the peak isn't a clean multiple of capacity, which is the difference between "enough capacity" and "one session short."