CS PAPERS DEEP-READ · PAPER 61
Rzadca et al. · University of Warsaw & Google · EuroSys 2020
In 2020 Google described Autopilot — a system that decides, on its own, how much memory and CPU every program gets, so engineers no longer have to. By the time of writing it already governed more than 48% of the resources used across Google's fleet, and it cut the share that is reserved but never actually used from 46% down to 23%. Counted in machines, that is tens of thousands of them.
The biggest waste in the cloud is usually not machines sitting idle. It is capacity that somebody reserved and then never touched.
You launch a service, and the platform asks you to fill in a form: how much memory does this need? how much CPU? Honestly — nobody knows. And the two ways of being wrong are wildly unequal. Ask for too little and your program gets killed the moment it exceeds its memory, or throttled to a crawl when it exceeds its CPU, and you are the one woken at 3am. Ask for too much and there is one more line on a bill nobody reads out loud.
So everybody asks for too much. Each person is being perfectly reasonable; added up, it is an astronomical amount of idling. Cloud waste comes less from scaling too slowly than from people being unable to fill in that form.
Autopilot's idea is so plain it is almost boring: stop making people guess. The system watches what every program actually uses, day after day, and fills the number in itself — and keeps changing it. Your electricity company does not ask you to declare next month's consumption; it reads your past usage and sets your tier.
Three steps, each of them close to ordinary common sense.
First, keep a ledger: for every program, a record of what it used in the past — in which the older entries fade. One freak spike from a Tuesday last month should not hold today's quota hostage forever.
Second, two resources, two different appetites for risk. For memory, take the historical peak: going over means being killed on the spot, and that is not a bet you can take. For CPU, take "the line it stays under almost all of the time": going over merely means running slower for a while, which is survivable, so there is no reason to pay for the highest spike ever seen. Then add a little safety headroom on top.
Third, when unsure, hold a vote. There is more than one sensible rule for picking that number — ledgers can fade faster or slower, headroom can be thinner or fatter. So Autopilot simply runs several rules at once and makes each of them replay the last few weeks: "if we had listened to you back then, how much trouble would you have caused, and how much would you have wasted?" Whichever rule has the best books gets the loudest voice. And changing its mind costs something too, so it does not flip between rule A and rule B day after day.
"Set the quota from past usage" is an idea anyone can have. What makes this paper valuable is that it worked in a real environment of a hundred thousand machines — and that engineers were willing to let go of the wheel.
The trick is that the asymmetry is written into the rules in plain sight: cutting too close is scored as far more expensive than wasting, so the scales are deliberately tilted. That comes with a careful rollout: simulate on history first, then run silently without taking effect, then a small controlled comparison, then a gradual handover with a one-click way out. The result is the counter-intuitive part: it did not kill more programs, it killed fewer — the number of programs badly hurt by running out of memory dropped by about ten times, because the numbers people had guessed were worse than the machine's estimates.
One honest caveat: it can only extrapolate from history. A program launching for the first time, or one whose shape suddenly changed, has no history to copy — it has to start from a conservative default.
Cloud waste is mostly not "scaling too slowly" — it is people guessing their resource requests wrong, because guessing low gets you punished and guessing high does not. Autopilot hands the form to a machine: read the fading history of real usage, take the peak for memory and a high percentile for CPU, add a safety margin, and let several rules replay history to see whose choices would have cost least. Google runs nearly half its fleet this way; waste fell from 46% to 23%, and programs were killed for memory less often, not more.
Want the decaying-histogram algorithm, the cost function behind the multi-model recommender, and the measured numbers? → Switch to the deep read
Autopilot is the autoscaling system Google runs on top of Borg. It sets each task's CPU and memory limits (vertical scaling) and each job's replica count (horizontal scaling) automatically: usage is accumulated into a histogram whose samples decay exponentially with age, a statistic is read off it (the peak for memory, a high percentile for serving CPU) and multiplied by a safety margin; in parallel, a pool of differently parameterised models replays history and votes, weighted by a "cost". By publication it covered more than 48% of Google's fleet-wide resource usage, cut slack — the gap between limit and actual usage — from 46% under human management to 23%, and reduced the number of jobs severely affected by OOMs by roughly an order of magnitude.
(limit − usage) / limit. The scheduler is holding it for you and nobody is using it. Shrinking it is what this paper is about.The paper is led by Krzysztof Rzadca (University of Warsaw / Google) with a Google team that includes John Wilkes, an author of the Borg paper, and appeared at EuroSys 2020. It follows directly from Borg (2015): Borg solved "which machines should these containers be packed onto", while Autopilot fixes the half Borg never touched — the numbers those containers asked for were wrong to begin with. Downstream, it is the reference point for every "rightsizing" practice in the cloud today: the recommender in Kubernetes' Vertical Pod Autoscaler is built on exactly this decaying-histogram / percentile / safety-margin recipe.
Systems like Borg make you declare a limit at submission time. Declare too little and your tasks get OOM-killed or CPU-throttled; declare too much and the scheduler holds that capacity for you and gives it to nobody else. The incentives are completely asymmetric: underestimating costs you an outage, a pager and a postmortem; overestimating costs you a line on a bill nobody audits.
So everyone overshoots — and then never revisits the number. The load changes, the code gets optimised, the traffic moves away, and that figure still sits where somebody set it in a panic two years ago. The paper's name for the resulting gap is slack, and for manually managed jobs it runs to 46%: almost half of what was reserved was never used.
Why can't Borg's existing resource reclamation fix this? Borg does hand the unused portion to low-priority batch work, but that only hides the waste: a high-priority job's limit is still a hard reservation, still occupies schedulable capacity, and still drives how many machines capacity planning has to buy. Reclamation treats the symptom; a smaller limit treats the cause.
Autopilot's pivotal move is not an algorithm. It is admitting that the two errors cost different amounts, and writing that down explicitly. Each candidate limit gets a cost that is roughly the sum of three terms:
With that function in hand, "what number should I put here" stops being a judgement call requiring nerve and becomes an optimisation you can replay against history and score. That is the hinge of the whole paper.
The first recommender is plain and effective. It samples each task's CPU and memory usage continuously and accumulates the samples into a histogram — not the raw time series, just the distribution, which is cheap to store and lets any percentile be read back at will.
The crucial twist is exponential decay: each sample's weight shrinks with age as w(τ) = 2^(−τ/t½), where τ is how old the sample is and t½ the half-life. In plain terms: the histogram fades, so a freak spike from a month ago does not hold today's quota hostage — but it isn't forgotten instantly either. The typical values the paper gives are a half-life of about 12 hours for CPU and about 48 hours for memory. Memory remembers longer, because forgetting too fast is paid for in dead tasks.
Which statistic you take depends on what happens when you exceed it:
The resulting number is then multiplied by a safety margin — on the order of ten percent, and relatively wider for small tasks, whose relative fluctuations are larger — and rounded up to a tidy value. Not a line of it is difficult mathematics. All of its weight is in one judgement: which resource deserves which appetite for risk.
Then there is applying the new limit. Because a changed limit can trigger a restart, Autopilot changes them in a rolling fashion — never every replica of a job at once — and is especially cautious about lowering memory: raising is always allowed, lowering demands stronger evidence from history.
The sliding-window recommender has knobs: how long a half-life? p90 or p95? how much margin? The best setting is not the same for every job — a service with flat traffic and one with two daily spikes need entirely different fade rates. Tuning knobs per job by hand would just move the form-filling problem somewhere else.
The second recommender (whose approach the paper describes as inspired by reinforcement learning) answers this by not picking one parameter set, but keeping a stable of models. Each model carries fixed parameters: its own decay rate, its own safety margin, its own way of turning the histogram into a limit. For each job the system replays the last few weeks of real usage and asks each model: "if we had followed you, how often would you have overrun, how much would you have wasted, how many times would you have changed the limit?" The cost function above turns that into a score, models with lower cost get more say, and the recommendation is their score-weighted combination.
One more penalty matters: switching models costs something too. Otherwise a hair's-breadth difference in scores would have the system following A today and B tomorrow, yanking the limit back and forth — and each yank can restart tasks.
Vertical scaling governs how big one replica is; horizontal scaling governs how many there are. The latter sets the replica count from a signal — CPU utilisation by default (total load divided by a target utilisation), or a custom metric the job owner supplies, such as queue depth or requests per second.
The instructive part is that it is deliberately asymmetric: scaling up follows recent load over a short window and reacts almost immediately, while scaling down looks at the peak over a multi-day window and descends one small step at a time. The reasoning is thoroughly practical — adding a replica you did not need costs a little money; removing one you did need leaves you caught out when the traffic returns. The same asymmetric-cost principle, applied a second time along the other axis.
Readers skip this section, and it is exactly what separates this paper from an algorithms paper. Taking quota control away from engineers is not technically hard; getting them to agree is. Autopilot's answer is a ladder of evidence: offline simulation on historical traces first; then a dry run in production that records what it would have set without applying anything, so owners can watch it against their own judgement; then controlled comparisons; then a phased handover with visible dashboards and a one-click way out at any moment. Small and new jobs are opted in by default, with opt-out available. This is engineering about how automation earns trust, not just about how a quota is computed.
First, it completes Borg's other half. Borg perfected the packing, but the input it packs — the limit each job declares — had always been wrong. No scheduler, however good, can rescue a systematically inflated order form. Autopilot fixes the input itself.
Second, it turns rightsizing from an operations craft into an optimisation problem. Once you can write a cost function and score candidates by replaying history, the work becomes automatable and continuous instead of depending on somebody's conscience one quarter. Kubernetes' VPA, cloud resource recommenders and FinOps tooling all walk this road — the VPA recommender is essentially the public implementation of this decaying-histogram / percentile / safety-margin recipe.
Third, and most underrated: it offers a template for how automation gets accepted. Simulate, dry-run, compare, phase in, allow exit — the path itself is reusable methodology.
① In one line: Autopilot sets CPU/memory limits and replica counts for every task on Borg automatically, from an exponentially decaying histogram of past usage plus a statistic plus a safety margin.
② The pain: cloud waste comes mostly not from slow scaling but from people mis-declaring resource requests — asking too little gets you killed or throttled, asking too much only shows on a bill, so everyone overshoots.
③ The hinge: write that asymmetry down as an explicit cost function (overrun ≫ waste, plus a churn penalty). "What number?" becomes a replayable, scoreable optimisation.
④ Vertical: usage accumulates into a fading histogram (w(τ)=2^(−τ/t½), ≈12 h for CPU, ≈48 h for memory); peak for memory, high percentile for serving CPU, average for batch; multiply by a margin, roll out gradually, lower memory only with care.
⑤ The multi-model recommender: keep a stable of differently parameterised models, score each by replaying history, weight by cost — and penalise switching so it does not oscillate.
⑥ Horizontal: the same asymmetry — scale up on a short window immediately, scale down on multi-day peaks in small steps.
⑦ Results: over 48% of Google's fleet-wide usage; slack 46% → 23% (in one comparison, from ~12,000 machines' worth to ~500); jobs severely affected by OOMs down about 10×, with >99.5% of job-days OOM-free.
⑧ Legacy and limits: Kubernetes VPA and cloud rightsizing tools follow this recipe almost directly — but it only copies history (cold start unsolved), the usage it observes is already distorted by throttling, changing limits can restart tasks, and the weights and hyperparameters are still set by humans. Automation moved the guessing; it did not end it.