The Budget That Governs Everything
Why is the race not to the swift?
Chapter 5 — The Budget That Governs Everything
“There is no such thing as a fast phone. There is only a phone that is fast right now.” — an industry adage
“the race is not to the swift, nor the battle to the strong” — Ecclesiastes 9:11 (ESV)
Why This Matters
Stop. Before you read another sentence about power, understand what this week actually is.
The first four chapters gave you a machine: the system-on-chip and The Four Questions; unlike cores under one scheduler; the memory hierarchy, where most of your performance was decided; and the accelerator fleet, with the discipline of asking which processor should run a workload at all.
This week you go back and read all four of them again, in a different language. Not “what does this block do” but “what does it cost, and how long can it keep costing it.” Every mechanism you have met was invented by an architect who could not buy watts. You have been learning the answers for four weeks. This is the week you meet the question.
That is a literal instruction about how to study this chapter, not a flourish. Section 5.1 walks Chapters 1 through 4 back through the energy lens, and by the end the strange asymmetric shape of a phone should stop looking like a collection of clever tricks. Efficiency cores, tile-based rendering, the NPU’s dataflow, the system-level cache, INT8 quantization, the sensor hub — those are not six ideas. They are one idea applied six times, and the idea is joules.
Here is the concrete form of it. A modern phone SoC can, for about a minute, dissipate a burst of power its chassis has no way to get rid of, because the chassis has heat capacity and heat capacity is a loan. Then the loan comes due: the skin approaches the temperature a human hand tolerates, the governor walks the clocks down, and the machine settles into a sustained operating point that may deliver less than half the work per second of the number in the review. Both numbers are real measurements. Only one describes the device you own. The AI workload put a spotlight on this — a wake-word detector is milliwatts on a tiny always-on core, but a language model generating tokens for ninety seconds is a sustained thermal event, and Chapter 4 promised you the reason a sustained NPU workload is not a peak one. That promise gets paid here, as does Chapter 2’s about energy-aware scheduling, because placement, frequency, and heat are one control problem, not three.
And the Christian question for the week is one the discipline keeps asking without noticing: why is the race not to the swift? Ecclesiastes says it flatly — “the race is not to the swift, nor the battle to the strong” (Ecclesiastes 9:11, ESV). Not a proverb about laziness: an observation about the gap between capacity and outcome, and every engineer who has watched a benchmark-winning chip lose a ten-minute workload knows what it names. Section 5.13 takes it seriously.
5.1 — Re-reading Chapters 1–4 Through the Energy Lens
Take the four chapters in order and ask one question of each: what was that mechanism buying, and what was the currency?
Chapter 1 — the instruction set and the SoC. AArch64 has a fixed 32-bit encoding, a load/store architecture, and 31 general-purpose registers, and RISC-versus-CISC is a second-order effect. Read those facts as an energy argument. Fixed-width decode is cheap decode, and decode runs on every single instruction — a small saving multiplied by a very large number, which mattered enormously when the whole core budget was around a watt. And speculation: a mispredicted branch throws away work on the order of ten to twenty cycles on a deep out-of-order core, depending on the microarchitecture. From the energy side, a mispredicted branch is joules spent producing nothing. Not slower — wasted. That is most of the reason an efficiency core is built shallower and narrower, and why efficiency cores were historically in-order or only lightly out-of-order (more recent ones are modestly out-of-order, but the design pressure is unchanged). Speculation is a bet paid for in energy, and an efficiency core has been told not to gamble.
Chapter 2 — heterogeneous multicore. The whole big.LITTLE-to-DynamIQ arc is an energy story pretending to be a scheduling story. Unlike cores on the same instruction set exist so a task can be placed where it costs the fewest joules for the performance it needs; per-core power and frequency domains exist so the expensive cores can be off — not idle, off; migration hysteresis exists because a migration costs a cold cache, a cold cache costs memory traffic, and memory traffic costs energy. Chapter 2’s scheduler was not choosing a core. It was spending a budget.
Chapter 3 — the memory hierarchy. This one reframes hardest. Chapter 3 gave you Mark Horowitz’s ISSCC 2014 keynote, “Computing’s Energy Problem (and what we can do about it),” and its widely quoted 45 nm figures: a 32-bit integer add on the order of 0.1 pJ, a 32-bit read from a small SRAM on the order of a few pJ, a 32-bit DRAM access on the order of a thousand-plus pJ — roughly three to four orders of magnitude more energy to move the number than to compute on it. As a performance fact that tells you to improve locality. As an energy fact it says something larger: the arithmetic is nearly free and the data movement is the whole bill. A 64-byte cache line you use four bytes of is energy spent on 60 bytes you did not want. Bandwidth is a power number wearing a performance costume.
Chapter 4 — the accelerators. Now the fleet snaps into focus. Tile-based rendering keeps a tile’s framebuffer traffic in on-chip memory and writes only the finished tile to DRAM — sold to you as a bandwidth saving, but the reason bandwidth is worth saving is Horowitz’s ratio. An NPU is a machine for not moving data: a dataflow arranged so a weight or activation, once fetched, is reused as many times as possible before going back to memory. Quantization from FP32 to INT8 is an exact 4× reduction in weight memory, and on a phone that matters because every byte not fetched is energy not spent. A fixed-function video encoder exists because the same codec in software burns orders of magnitude more energy for the same output.
So here is the thesis of the course in one line, and it is the line Chapter 8 will grade you on:
Every strange, asymmetric, specialized thing about a system-on-chip is an answer to a question about joules, asked under a heat limit set by a human hand.
Coach’s Note — If you take one habit from this chapter, take this: whenever you meet new mobile silicon, ask “what would this cost in energy if the general-purpose core did it instead?” That question explains more block diagrams than any amount of reading about pipelines — and it predicts the next accelerator, which is always the workload that is currently expensive, common, and stable enough to freeze in silicon.
The Four Questions, restated for this week: performance is what you deliver, energy is what it costs, thermals are how long you may keep spending, placement is your main lever on both. From here on, an answer that addresses only question one is an incomplete answer.
5.2 — Where the Watts Go: Dynamic Power and the Leakage That Made Idle a Problem
A digital circuit spends power two ways, and they behave nothing alike.
Dynamic power is the cost of switching. Every time a gate’s output flips it charges or discharges capacitance, and that charge comes out of the supply:
P_dyn ≈ α · C · V² · f
- α — the activity factor: what fraction of nodes actually switch per cycle. The term software controls.
- C — switched capacitance: how much wire and gate you drag up and down. A wider machine has more; so does a longer wire, which is why moving data across a chip is expensive and off-chip is worse.
- V — supply voltage. Note the square. The most important exponent in mobile computing.
- f — clock frequency. Linear. Also the number printed on the box, which is why students over-index on it.
Static power — leakage — is what transistors dissipate simply by being powered on. Two properties matter. First, leakage does not care whether you are doing anything: it is billed per second, not per operation. Second, leakage grows with temperature and grew as process nodes shrank — a hot chip leaks more than a cool one, a positive feedback loop thermal design must actively suppress.
That second fact is why idle became a design problem. When leakage was negligible, a chip that was not switching cost nothing. Once leakage became a meaningful share of the budget, an idle block became a block quietly spending your battery to sit still — and a phone spends the overwhelming majority of its life not being used. If idle is not nearly free, nothing else you do matters. Everything in §5.5 answers that one sentence.
5.3 — The Cubic Intuition and the Efficiency Knee
Hold α and C fixed and raise f. Over the range where the required supply voltage must rise along with frequency — most of the useful range — V scales roughly with f, so:
P ∝ V² · f and V ∝ f ⟹ P ∝ f³ (approximately)
This is an approximation with a named assumption, and you must say the assumption aloud every time you use it: it holds only while voltage has to track frequency. Below some point voltage bottoms out at a floor set by reliability and by memory cells’ need to hold state, and further clock reduction buys a roughly linear saving instead. Real silicon has a discrete table, not a curve. But the intuition is sound: the last 20% of clock speed can cost far more than 20% of the power. That is why the top operating point exists mostly for bursts.
Now the more useful idea. The cubic intuition is about power; convert it to energy per unit of work and something better appears. For a fixed task of N cycles at frequency f, run time is N/f, so:
E_dynamic = P_dyn · t = (k · V² · f) · (N/f) = k · V² · N
The f cancels. Dynamic energy per task depends on voltage, not on frequency. What actually changes with frequency is the other term:
E_static = (P_leakage + P_platform) · t = (P_leakage + P_platform) · N/f
Everything billed per second — core leakage, the memory controller, SRAM retention, the always-on rails, and, if the user is looking at the screen, the display — gets more expensive the longer you take. Two terms pulling opposite ways: a V² term punishing speed, a per-second term punishing slowness. Their sum has a minimum, and that minimum is the efficiency knee: the operating point at which a fixed task costs the fewest joules. Above it you pay voltage for speed you did not need; below it you pay rent on a platform idling while you dawdle. This is arithmetic, so do it rather than assert it — build and run code/energy_model.cpp:
g++ -std=c++17 -O2 -o energy_model code/energy_model.cpp
./energy_model
work quantum : 3.00 Gcycles
platform power : 0.90 W (paid every second the task runs)
idle power : 0.05 W (deep-idle draw after the task finishes)
f(MHz) V t(s) P_dyn P_stat E_dyn E_stat E_task(J)
------ ----- ------ ------ ------- ------- ------- ---------
800 0.70 3.750 0.47 0.95 1.764 3.563 5.327
1200 0.78 2.500 0.88 0.97 2.190 2.425 4.615
1600 0.85 1.875 1.39 1.00 2.601 1.875 4.476
2000 0.93 1.500 2.08 1.04 3.114 1.560 4.674
2400 1.01 1.250 2.94 1.10 3.672 1.375 5.047
2800 1.08 1.071 3.92 1.18 4.199 1.264 5.463
3000 1.12 1.000 4.52 1.24 4.516 1.240 5.756
EFFICIENCY KNEE: 1600 MHz at 4.476 J/task.
The top point (3000 MHz) finishes 0.88 s sooner and costs 28.6% more energy for the same work.
The slowest point (800 MHz) costs 19.0% more than the knee — crawling is not free either.
Read the E_dyn column: it rises with voltage and never cares about frequency directly. Read E_stat: it falls as the run shortens. Their sum is a shallow U with a floor at 1600 MHz. The top of the table nearly doubles the clock, saves 0.88 seconds, and spends 29% more energy to do it. Those operating points are a synthetic teaching model, not a datasheet — the shape is right and the arithmetic exact, the numbers illustrative. That distinction, modeled versus measured, is graded in Week 8.
Note the second bullet the program prints: the slowest point costs 19% more than the knee. Every student’s first instinct on hearing “energy” is “run everything as slow as possible,” and it is wrong for a reason worth internalising — you are not the only thing spending the battery. Slowness is not free. It is rent.
5.4 — DVFS: Operating Points, Governors, and the Scheduler’s Other Lever
Dynamic voltage and frequency scaling walks a core up and down that table at run time. The hardware exposes a discrete set of operating performance points — validated (frequency, voltage) pairs. Not a continuum: a menu, characterised in silicon.
On Linux, and therefore on Android, the subsystem is cpufreq and the policy is a governor. Look at yours:
cd /sys/devices/system/cpu/cpu0/cpufreq
cat scaling_available_governors scaling_governor # the menu, and who is in charge
cat scaling_available_frequencies scaling_cur_freq # the OPP menu, and where we are
cpupower frequency-info # the friendly summary, if installed
You will meet performance (pin the maximum), powersave (pin the minimum), the older ondemand and conservative (raise the clock in response to measured utilisation), userspace (a program decides), and — the modern one, and the one that matters architecturally — schedutil.
schedutil matters because of where its input comes from. Older governors sampled utilisation on a timer from outside the scheduler; schedutil is driven by the scheduler’s own utilisation signal, the same per-entity load tracking used to decide placement. That is the bridge back to Chapter 2: placement and frequency stopped being two subsystems and became one control problem. Section 5.11 adds the third lever — heat — to the same loop.
On Apple platforms the same policy exists but is not exposed as a tunable: you express intent through Quality of Service classes and the system decides — the same deliberate refusal you met in Chapter 2 with CPU affinity, and the same trade.
To see DVFS rather than read about it, run watch -n 0.5 'grep MHz /proc/cpuinfo' on Linux (or sudo powermetrics -n 3 -i 1000 --samplers cpu_power on recent macOS), put one core under load, and watch the frequency climb and fall. That is the governor in your hands — and if you are plugged in, notice how differently it behaves on battery. Rep 3 makes you do it properly.
5.5 — Clock Gating, Power Gating, and Break-Even Residency
Return to §5.2’s two terms. There are two corresponding ways to stop spending.
Clock gating stops the clock to a block with nothing to do. With no edges, nothing switches; α goes to zero and dynamic power collapses. Leakage does not: the block is still powered, still holding state, still trickling. Entering and leaving costs a cycle or two, so it happens constantly, in hardware, without software knowing.
Power gating cuts the supply entirely, so leakage goes away too — but an unpowered block forgets everything. Entering costs the energy and time to save whatever state must survive; leaving costs restoring it, refilling caches, relocking PLLs. Expensive on both ends.
That asymmetry gives you the concept that organises every sleep state in every computer:
Break-even residency is the minimum time a block must stay in a low-power state for the energy saved while it is there to exceed the energy spent entering and leaving. Sleep for less than that and you have spent energy to save energy.
T_break-even = E_transition / ΔP
If a deeper state costs 3 mJ to enter and exit and saves 0.4 W, you must stay in it at least 7.5 ms to come out ahead. A 4 ms nap is a net loss — you paid 3 mJ to save 1.6 mJ. This is why processors expose a ladder of idle states rather than one: shallow states with tiny entry costs for short gaps, deep states with large entry costs for long ones. The kernel’s idle governor’s whole job is predicting the gap and choosing the rung.
Look at your own ladder. On Linux each state under /sys/devices/system/cpu/cpu0/cpuidle/state*/ publishes a name, an exit latency, a required residency, and a usage count, all in microseconds. Those residency values are the platform’s own break-even numbers: you are reading the arithmetic above as a table someone already computed for your silicon. Rep 3 walks you through printing them.
This is also the deep reason a phone hates being woken: a wakeup pulls a core out of a deep state, lights up the memory controller, possibly wakes the radio. Ten background apps each checking in once a minute on their own schedule cost far more than the same ten checking in together every ten minutes — same bytes, one wakeup instead of sixty. Chapter 6 runs this argument again on the radio, where it produces the rule that batching beats compressing.
Coach’s Note — Break-even residency is the most transferable idea in this chapter. It is the same shape as a cache line (“you pay for 64 bytes whether or not you want them”), as a context switch, as a cold start. In every one the question is: does the thing I am about to enter last long enough to repay what it cost to get in? Ask that of every optimization anyone proposes to you.
5.6 — Energy Is the Currency: Joules per Task and the Honest Race-to-Idle
Say it plainly, because the week turns on it:
Power is not the currency. Energy is.
E = P × t. A battery holds joules. Halving power while tripling run time is a 50% energy increase dressed up as an efficiency win.
The correct metric for fixed work is joules per task; for a stream, performance-per-watt.
Which brings us to race-to-idle, the most over-quoted heuristic in mobile computing. The claim: because leakage and platform overheads are billed per second, finishing fixed work quickly at a high operating point and then dropping into a deep sleep state uses less total energy than crawling. Sprint, then sleep. Sometimes true — but routinely stated as a law, and it is not one. Let the model settle it; energy_model.cpp computes total energy over a fixed window as task energy plus idle energy for the remainder.
RACE-TO-IDLE over a 4.0 s window (task energy + idle energy for the remainder)
f(MHz) t(s) E_task E_idle E_window
------ ------ ------- ------- ---------
800 3.750 5.327 0.013 5.339
1200 2.500 4.615 0.075 4.690
1600 1.875 4.476 0.106 4.582
2000 1.500 4.674 0.125 4.799
2400 1.250 5.047 0.138 5.185
2800 1.071 5.463 0.146 5.610
3000 1.000 5.756 0.150 5.906
Lowest window energy: 1600 MHz. The same point as the joules-per-task knee.
Racing all the way to the top loses: 5.906 J against the knee’s 4.582 J — 29% more energy to save 0.88 seconds. But look at the other end. Crawling at 800 MHz costs 5.339 J, 17% more than the knee, so racing up to the knee genuinely wins. Race-to-idle is directionally correct up to the knee and wrong past it — not a law, but the left-hand side of a U-curve mistaken for the whole curve.
Now change one input — the cost of being awake. Run ./energy_model --platform 3.0, the case where the screen is lit and the radio is active, and the knee climbs from 1600 MHz to 2400 MHz while the penalty for crawling at 800 MHz goes from 19% to 72%. Nothing about the core changed; the rest of the system got expensive, so time got expensive, so finishing early became worth paying voltage for. The optimal operating point is a property of the whole platform, not of the core. Run ./energy_model --window 1.2 and a third answer appears: with a deadline that tight the knee misses it entirely, and the window — not energy — picks the point. You are buying time with joules, deliberately, and you should say so.
So the honest statement, the one you should be able to give in an interview. Race-to-idle wins when the work is fixed and finite, a genuinely deep idle state exists, being awake is expensive relative to the core, and the target point is at or below the knee. It loses when the point is past the knee, when the workload is a continuous stream with no idle to race to (video playback, sustained inference, a game rendering forever), when something else keeps the platform awake anyway, or when going fast heats the device into throttling and you end up slower and poorer. That last one is the mobile-specific trap, and §5.8 through §5.10 are about it.
Coach’s Note — When somebody says an optimization saved power, ask two questions before you congratulate them. Did total energy for the task go down, or just the instantaneous watts? And over what window, with what else running? I have watched a team celebrate a 20% power reduction that was a 35% energy regression because the job took twice as long. Both numbers were measured correctly. Only one was the question.
5.7 — The Battery: mAh, Watt-Hours, and Who Actually Spends Them
Everything above spends from one account. Cell capacity is quoted in milliamp-hours, which is a charge, not an energy. Charge becomes energy only when you multiply by a voltage:
watt-hours = mAh × nominal volts / 1000
Exact arithmetic, and it settles the most common confusion in the field. Is a 5000 mAh battery bigger than a 4500 mAh one? Only at the same nominal voltage. Watt-hours is the comparable number, which is why regulators and airlines ask for it. Here is the worked example, with the voltage hedged as it should be — single-cell lithium-ion nominal voltages are commonly quoted somewhere in the 3.7–3.9 V range, and the real figure is on the cell’s datasheet:
5000 mAh × 3.85 V / 1000 = 19.25 Wh = 19.25 × 3600 ≈ 69,300 joules
Convert to joules once and stay there: a joule is a watt-second, and every other number in this chapter is in watts and seconds, so everything adds up in one unit. Put §5.3 next to it — the modeled task cost about 4.5 J at the knee, so this battery holds roughly fifteen thousand of them, assuming absurdly that nothing else in the device draws anything.
Which is the point of code/battery.py: nothing else in the device draws nothing.
python3 code/battery.py --profile reading
battery : 5000 mAh at 3.85 V nominal = 19.25 Wh (69300 J)
profile : reading
subsystem watts share
------------ ------- ---------
display 0.90 65.7%
soc 0.35 25.5%
modem 0.05 3.6%
sensors 0.02 1.5%
platform 0.05 3.6%
TOTAL 1.37 100.0%
runtime : 14.05 h (843 min) from a full charge at this steady draw
dominant : display at 0.90 W — 66% of the budget
Those subsystem draws are a synthetic teaching model — read the header comment and replace them with measurements before using this for anything real. But the ordering is the lesson, and it is robust: on a device whose screen is on and whose CPU is barely working, the display is a budget-scale actor and the SoC is not. Run --profile sustained-inference and the ordering flips hard; --profile navigation and the modem and positioning sensors take a share nobody expected. You will spend a career being handed problems phrased as “make the SoC more efficient,” and sometimes the SoC is a quarter of the budget and you are optimizing the wrong quarter.
Four battery behaviours to know, stated structurally because their magnitudes are chemistry- and vendor-specific. Terminal voltage sags under load and falls as the cell depletes — nominal voltage is an average, not a constant. Capacity fades with cycles, so a two-year-old device works with a smaller budget. Cold hurts: internal resistance rises, capacity drops, the sag worsens. And charging is itself a thermal event, which is why a phone slows while charging and why benchmarking plugged in is a different experiment from benchmarking on battery.
5.8 — The Phone Has No Fan
Here is the sentence this chapter exists to make you believe:
A phone has no fan, and its thermal limit is set by what a human hand can hold, not by what the silicon can survive.
No fan. A plugged-in machine moves heat by forced convection: a fan pushes air across a heatsink with enough surface area to hand the heat off. A phone cannot — no fan, no room for one, no acoustic or power budget for one. Its entire thermal path is die → package → thermal interface material and a vapour chamber or graphite spreader → chassis → convection and radiation off the outside surface. Every stage is a thermal resistance, and the last — a smooth surface, still air, a modest temperature difference — is a bad one.
The limit is your hand. Silicon can run hot; junction temperatures well above the boiling point of water are survivable for a die. But the outside of the device is being held, pressed against a face, put in a pocket against skin, and product-safety standards govern how hot a touchable surface may get. That skin limit, not the junction limit, caps sustained power. The silicon is nowhere near its limit when a phone starts throttling. The user is.
Steady-state dissipation is roughly ΔT / R_thermal — how much hotter than the room you may be, divided by how hard it is to get heat out. Both terms are small and neither is negotiable, so sustained dissipation for a phone-sized device lands on the order of a few watts, against tens for a fanless laptop and tens to hundreds for a plugged-in machine. Treat those as order-of-magnitude shapes, not specifications; the real figure depends on chassis material, spreader design, ambient, and whether the device is in a case, a hand, or on a table.
That constraint radiates outward. It is why specialization won: if you cannot spend more watts, the only way to deliver more work is more work per watt. It is why form factor determines architecture: a wearable has a fraction of a watt and millimetres of path, a tablet more area and mass, a head-worn device a weight limit and a face against it — same vendors, different envelopes, different right answers. It is why any peak throughput figure is a claim about a moment. And it is why a case, a hot car, or direct sunlight measurably changes performance — raise the ambient and you shrink ΔT, the numerator.
Coach’s Note — The best question I know for interrogating a mobile design is: where does the heat go, and how fast can it get there? A block diagram tells you what the chip can do; a thermal path tells you how long it can do it. Architects who look only at the first design products that review beautifully and disappoint in the second minute.
5.9 — Thermal Mass: Why the First Run Measures the Chassis
If sustained dissipation is a few watts, why does a phone burst well above that? Because heat takes time to arrive. The chassis has thermal mass — heat capacity — and a body with heat capacity absorbs energy while its temperature rises. For the first tens of seconds of a heavy workload, most of the energy you dissipate goes into warming the device up rather than escaping it. Skin temperature has not reached the limit, so nothing throttles, so the clocks stay high.
T(t) = T_ambient + R·P · (1 − e^(−t/τ))
R is thermal resistance to ambient in °C/W, P the dissipated power, τ = R·C_thermal the time constant. Everything about mobile benchmarking follows from the existence of τ. A cold device is a device with its whole thermal budget unspent — which is why a benchmark’s first run is fast and its tenth is not.
Go look at the shape. code/thermal-soak.csv is a fifteen-minute sustained-load soak of a modeled phone-class SoC, sampled every ten seconds. Its first line says exactly what it is: # SYNTHETIC TEACHING DATA — modeled on published behavior, not a measurement of any specific product. Read that and mean it. It came from a first-order thermal model like the one above, driven by a governor stepping operating points against a forecast skin temperature. Never cite it as a measurement of a product. What it is good for is teaching you to read the shape, because the shape is real.
python3 code/throttle_analysis.py
thermal soak analysis — thermal-soak.csv
run length : 900 s, 91 samples
peak clock : 3000 MHz
peak power : 7.40 W peak SoC 87.2 C peak skin 43.2 C
throttle onset : t = 80 s (clock 2600 MHz, skin 41.7 C)
sustained clock : 1200 MHz (median of last 300 s)
sustained power : 2.78 W (median of last 300 s)
Three phases, nameable on sight in any soak curve you will ever see:
- The burst (0–70 s). Full clock, 7.40 W, skin climbing from 30 °C. Nothing throttles because nothing is hot yet. The device is spending its thermal loan.
- The knee (80–130 s). Skin approaches the limit, the governor steps operating points down, delivered work falls off a cliff — 3000 MHz to 1200 MHz in about fifty seconds.
- The sustained state (130 s onward). Power settles near 2.8 W, skin hovers just above 42 °C, and the clock hunts between two adjacent operating points forever. This is the machine. Everything before was a transient.
Peak power against sustained power: 7.40 W versus 2.78 W. The device dissipated 2.7× its sustained capability for over a minute — because the chassis was cold and the heat had somewhere to go. That is thermal mass doing exactly what §5.8’s arithmetic says it should.
5.10 — Throttling, and Sustained Versus Peak as an Honesty Problem
The same script prints this:
THE HONESTY LINE
a 60 s benchmark would report : 100.0 (mean score, first 60 s)
the sustained truth is : 41.9 (mean score, last 300 s)
sustained / peak : 0.42 — the short run overstates by 2.38x
A sixty-second benchmark on this modeled device reports a perfect score, and it is not lying. The device really did deliver that; nothing was falsified. And the same device, doing the same work for ten minutes, delivers about 42% of it.
Peak and sustained are not two estimates of one quantity. They are answers to two different questions. “How fast is this device when cold and unthrottled?” is the right question for genuinely bursty work — unlocking, launching an app, taking a photo, one turn of a conversation. “How fast is it after ten minutes?” is the right one for a game, a video export, a navigation session, or a model generating a long response. Quote one when the reader needs the other and you have misled them with a true number.
Quote both, or quote neither — and always state the conditions. Cold or soaked. Plugged in or on battery. Ambient. Case on or off. Which run of how many. A single number without its conditions is not a measurement; it is an advertisement.
Two consequences.
Throttling is not a defect. Students read a falling clock as failure; it is the device working, trading performance to keep a surface at a temperature a person can hold. A device that did not throttle under this load would be a device that burned someone. The engineering question is never “why is it throttling” but “what is its sustained envelope, and did we design the workload for that or for the transient?”
And this is where Chapter 4’s promise gets paid. The placement table you built there was implicitly a table of peak behaviour, and sustained work plays by different rules. Take an on-device language model generating tokens for ninety seconds. Chapter 3 told you it is usually memory-bound rather than compute-bound; Chapter 4 told you the NPU exists to avoid moving data. Now add this week: it is also a sustained thermal event, sharing one envelope with the display showing the output and the memory subsystem streaming the weights. The NPU being more efficient per token does not exempt it from the envelope — it widens the envelope’s usefulness, a different and more valuable claim. A placement decision made on peak numbers alone will be wrong for exactly the workloads that matter most, which is why this chapter’s project asks you to defend an operating policy in joules rather than milliseconds.
Coach’s Note — Meet every performance claim with three questions: For how long? Under what conditions? Compared to what? If any is missing the claim is incomplete — not necessarily dishonest, but incomplete in the direction that flatters the claimant. That asymmetry is not an accident, and noticing it is most of what separates an engineer from an audience.
5.11 — The Platform Asks the Application: Thermal Status, Headroom, and Energy-Aware Scheduling
Here is the architecturally surprising part, and the reason this is not purely a hardware chapter.
You might expect thermal management to be entirely hardware and firmware: sensors, a governor, clocks come down. That layer exists — on Android the mitigation machinery lives in the thermal HAL and thermal-engine-style daemons, below anything an app can see. But modern platforms do something else as well. They tell applications about the thermal state and ask them to shed load themselves.
On Android the API is small and worth knowing precisely. PowerManager.getCurrentThermalStatus() returns the current status constant and addThermalStatusListener() registers a callback for changes. The ladder is THERMAL_STATUS_NONE, LIGHT, MODERATE, SEVERE, CRITICAL, EMERGENCY, SHUTDOWN — increasing severity, with the platform taking increasingly drastic action of its own as you climb. And getThermalHeadroom(int forecastSeconds) returns a normalized forecast of how close the device will be to throttling that many seconds ahead, where 1.0 is the throttling threshold.
Read that last one twice: it is a forecast and it is normalized, and both choices are deliberate. Normalizing hides device-specific temperatures an app has no business interpreting and could not compare across products anyway. Forecasting exists because thermal control has a time constant — by the time the temperature is at the limit you are already too late, since the heat now in the die is still on its way to the surface. An application that waits for SEVERE has already contributed to the problem it is responding to.
Think about what that admits. The platform cannot manage the thermal budget well by itself, because only the application knows which of its work is discretionary. The governor can lower a clock; only the app knows it could render at a lower resolution, drop from 120 frames per second to 60, defer a background sync, encode at a lower bitrate, or run the smaller model. The governor’s tool is blunt and global; the app’s is precise and local. So thermal management is a hardware/software contract, not a hardware behaviour — an architecture fact that belongs in your SoC Architecture Review alongside the block diagram. On an Android device with developer options you can watch the ladder with adb shell dumpsys thermalservice; that is a Workbench D step, optional and never required for a graded item.
And now Chapter 2’s loop closes. Linux’s Energy Aware Scheduling places a task by consulting a per-platform energy model — the cores, their operating points, what each costs — and choosing the placement that delivers the required performance for the fewest joules. Utilisation is tracked with per-entity load tracking; uclamp lets userspace hint a floor or ceiling on a task’s perceived utilisation; Android additionally partitions tasks with cpusets into groups such as top-app, foreground, and background.
| Lever | Question it answers | Chapter |
|---|---|---|
| Placement (which core, which processor) | Where should this run? | 2 and 4 |
| DVFS (which operating point) | How fast should it run? | 5.4 |
| Thermal mitigation and app-level shedding | Are we still allowed to? | 5.11 |
Three levers, one budget, one controller. A scheduler that placed tasks without an energy model would be answering question one of The Four Questions and ignoring the other three. That is the whole reason energy-aware scheduling exists — and why Chapter 2’s promise had to wait for Chapter 5’s vocabulary.
5.12 — Interactive Lab: The Thermal Envelope Simulator
Below this chapter on the website you will find The Thermal Envelope Simulator. It is not an extra; it is where §5.8 through §5.10 stop being prose.
You control sustained workload intensity, the thermal envelope, ambient temperature, and chassis thermal mass, then run a simulated ten-minute soak and watch clock, power, temperature, and delivered work evolve together. A marker shows what a sixty-second benchmark would have reported next to what the ten-minute run delivered.
Do these four experiments in order, and predict each result before you run it:
- Baseline. Defaults, full soak. Find §5.9’s three phases and write down the sustained/peak ratio. At what second did the burst end, and what physically ended it?
- Raise ambient by ten degrees. Shorter burst, lower sustained level, or both? The answer names which term in
ΔT / Ryou changed — and why your phone is slower in a hot car. - Increase thermal mass, hold everything else. Predict the effect on the burst and on the sustained state separately; they are not the same, and this is the most instructive knob on the panel.
- Drop intensity below the sustained envelope. Nothing throttles; the ten-minute number equals the sixty-second one. What does that tell you about benchmarks that never throttle?
Then open the second panel: DVFS policy. Choose among race-to-idle, a fixed mid-point, and a thermally-aware policy, and compare total work completed and total joules over a fixed window. You are looking for the case — deliberately present — where the aggressive policy completes less total work, because it spent its thermal budget early and paid with a deeper throttle later. Leave this widget able to sketch a soak curve from memory and label its three phases and you have the week.
5.13 — The Race Is Not to the Swift
Ecclesiastes 9:11 (ESV) says that “the race is not to the swift, nor the battle to the strong.” The Preacher is not offering encouragement. He is reporting something he has observed and finds hard: capacity and outcome come apart. Something intervenes between what a thing can do and what actually happens, and the gap is not a rounding error. It is a pattern.
Any mobile architect knows that gap intimately, because measuring it is the job. A device’s peak score is a true statement about its capacity and, very often, a false statement about its life. The gap is not fraud; it is time. There is a discipline hidden in that, worth naming as more than technique. Endurance is a different virtue from speed, and the world mostly runs on the first one. The device that finishes the ten-minute encode is not the one with the highest first-minute score; it is the one whose designers accepted a lower peak so the sustained state would be higher, knowing the review would punish them for it. A small, real act of integrity. The Lutheran tradition has a name for that kind of choosing: vocation, the ordinary work God has actually given you, done for the neighbour who will actually use it. The neighbour here is a person on a bus, in the fourth hour of a battery, on a two-year-old device, in a warm room, holding this thing in one hand. That person is never in the benchmark’s conditions. Designing for them, and reporting honestly to them, is the whole of the vocation — quiet work no leaderboard rewards.
Which is where the apologetic edge is, and I want it sharp. A peak score is a true number about a false situation. That is more dangerous than a lie, because a lie can be refuted and a true-but-unrepresentative number defends itself: everyone repeating it is telling the truth, and the distortion lives in what was omitted — the conditions, the duration, the state of the device. A measurement stripped of its conditions is a form of false witness that never has to say anything false.
The same move applies to self-assessment, which is where the Preacher was aiming. It is easy to know your peak — the day you performed, the project that went well. Your sustained state is harder to look at and more honest: what you deliver in the fourth hour, in the tenth week, when nothing is new and the chassis is warm. “The race is not to the swift” is not a consolation for the slow. It is a warning to the swift, that capacity is not fruitfulness, and that a life measured in bursts is measured over the wrong window.
Engineers are the people who choose the window. Choose it honestly.
5.14 — Common Pitfalls
Pitfall: Reporting a peak number as if it described the device.
Example: “This SoC scores 100 on the sustained-compute test” — one sixty-second run on a cold device, when ten minutes delivers 42.
Fix: Quote both, or quote neither. Warm to steady state, then measure; run long enough to cross the knee; report a median with dispersion and the conditions — run length, cooldown, ambient, plugged in or not, which run of how many. Appendix C’s soak protocol exists for exactly this; run code/throttle_analysis.py on your own log and print the honesty line before you publish.
Pitfall: Optimizing power when the thing that matters is energy.
Example: A change halves instantaneous watts and is celebrated — but the task now takes 2.5× as long, so joules per task went up by 25%.
Fix: E = P × t. For fixed work report joules per task; for a stream, performance-per-watt. State which you chose and over what window. A watts-only claim is not an efficiency claim.
Pitfall: Treating race-to-idle as a law.
Example: Pinning a task to the top operating point “so it can sleep sooner,” on a workload with no deep idle to fall into anyway.
Fix: Run code/energy_model.cpp for your own case. Racing wins below the knee and loses above it, and the knee moves with platform power and deadline. Check that a deep idle state exists and that the work is finite before racing toward it.
Pitfall: Comparing batteries by mAh.
Example: “5000 mAh beats 4500 mAh” — asserted across two devices with different nominal cell voltages.
Fix: Convert. Wh = mAh × V / 1000, then to joules. Compare watt-hours against the whole system’s draw — code/battery.py shows which subsystem dominates.
Pitfall: Reading throttling as a hardware defect, or optimizing the SoC when the SoC is not the budget. Example: “The clock dropped from 3000 to 1200 MHz — the chip is failing.” Or: weeks spent shaving 15% off a background service on a workload where the display is two-thirds of the draw. Fix: Throttling is the platform holding a surface to a temperature a hand can tolerate; the real question is the sustained envelope and whether your workload was designed for it. And measure the whole system before choosing a target — sometimes the right answer is “dim the screen and defer the sync.”
5.15 — Reps
Open the exercises and do all of them. This week’s reps build the reflexes the project grades: deriving the operating-point table by hand, moving the efficiency knee on purpose, finding the throttle onset in a soak log, running a real sustained-load experiment, and defending a policy in joules.
This week’s AI policy. Use a model to explain — the cubic approximation, break-even residency, what a governor does. Never use one to source. A thermal design power, a battery capacity, a nominal cell voltage, or an operating-point table comes from a vendor specification page, an architecture reference manual, or your own measurement, and you cite it. A language model will produce a confident, entirely invented sustained-power figure in a tone indistinguishable from a datasheet, and a fabricated figure in a submitted report is an integrity failure, not a deduction.
A preview:
- Rep 1 — Derive the operating-point table by hand and find the efficiency knee before you let
code/energy_model.cppcheck you. - Rep 3 — Read your own machine’s governor, its available frequencies, and its idle-state ladder from the operating system.
- Rep 5 — Compute break-even residency for a sleep state and decide whether a given nap is a net loss.
- Rep 7 — Find the throttle onset in
code/thermal-soak.csvand compute the sustained-to-peak ratio. - Rep 9 — Report the same sixty-second run three honest ways, and say which one a buyer needs.
A short “Check Your Reps” quiz is embedded on this page below the lab. It is an ungraded self-check; take it before you move on. Then sit this week’s graded knowledge check in Canvas — worth 1.5%, same material, larger pool, randomized draw, so retaking it is real practice rather than memorization.
5.16 — This Week’s Project
You are ready for Project 5 — The Energy and Thermal Study, in Project 5.
A note on numbering, so nobody thinks a file is missing. There is no Project 4. Week 4 was the checkpoint week: it carried the auto-graded cumulative checkpoint across Weeks 1–4 and the Placement Practical instead of a weekly lab. So the projects run 1, 2, 3, then 5 — and 5 is this one.
The Normal tier has three parts. You run code/energy_model.cpp, locate the efficiency knee, then move it on purpose and explain in energy terms why it moved. You analyse code/thermal-soak.csv with code/throttle_analysis.py and report the throttle onset, the sustained state, and the honesty line. Then you run a real sustained-load experiment on your own machine under Appendix C’s soak protocol and report the throttling you can actually observe — including, legitimately, “none, and here is why that is itself a finding.” The graded core is the reasoning: given a delivered-work target, choose an operating policy and defend it in joules. Medium adds a battery-budget analysis with code/battery.py. Hard is the judgment piece an assistant cannot do for you: raise a product’s thermal envelope by 20% or improve its performance-per-watt by 15%, and defend the choice with arithmetic. Every Normal requirement is completable on Workbench B; like every lab it is worth 7% of your grade, and the contract is in Appendix D.
5.17 — Coach’s Final Word
You came into this week with a machine and you are leaving with a budget.
You can now write down where the watts go and say which term software controls, and explain why voltage is squared and frequency is not — why the last 20% of clock is the most expensive 20%. You can find an efficiency knee and, more importantly, move it, because the optimum operating point is a property of the whole platform, not of the core. You can compute a break-even residency and tell a profitable nap from an expensive one. You can convert a battery rating into joules and discover the display was two-thirds of the budget all along. You can name a soak curve’s three phases on sight. And you can say precisely why the sixty-second number and the ten-minute number are both true and only one of them is honest.
That last skill is the one I would keep if I could keep only one.
Look back at §5.1 now that you have the vocabulary. Every mechanism in Chapters 1 through 4 — the cheap decode, the shallow efficiency core, the cache line, the tile buffer, the NPU’s dataflow, the INT8 weight, the fixed-function encoder, the sensor hub — is a joule argument. They always were. This week you learned to read them in the original. Chapter 6 will show you an interconnect and a modem that are also joule arguments; Chapter 7, that security mitigations are paid for in the same currency; Chapter 8 will hand you the instruments and ask whether you can be trusted with them.
And the race is not to the swift. Not because speed is worthless — it is a real gift — but because capacity is not fruitfulness, and the window you measure over decides which one you are looking at. The engineer’s version of that wisdom is a habit, not a mood: quote both numbers, state the conditions, design for the fourth hour rather than the first minute. The person holding the device is never in the benchmark’s conditions. Build for the person.
Do the reps. Run the soak. Watch the clock fall, and write down the honest number.
See you next week.
Up next: Work through the exercises — every rep, in order, predicting before you measure — then take on Project 5, the Energy and Thermal Study. Toolchain and workbenches: Appendix A. Dataset provenance: Appendix B. Soak protocol and statistics: Appendix C. Grading and AI policy: Appendix D. Vocabulary: Appendix E. Then Chapter 6 — wiring the system together, where the interconnect decides who waits, and where you meet the modem: a computer running its own operating system next to yours.
Previously: Chapter 4 — beyond the CPU: the accelerator fleet, tile-based rendering, and the placement decision. This week paid its promise about why a sustained NPU workload is not a peak one.
Week 5 Knowledge Check
T = E_transition / delta_P, so 3 mJ divided by 0.4 W is 7.5 ms. Four milliseconds of residency saves 0.4 W times 0.004 s, which is 1.6 mJ, against 3 mJ spent getting in and out: a net loss of 1.4 mJ. The tempting wrong answer is the first one, the intuition that sleeping is free; it is not, and that is exactly why processors expose a ladder of idle states instead of one. The fourth option overcorrects: a 40 ms gap in the same state is a clear win. E_dyn column rise as you move down the table? f(MHz) V t(s) E_dyn E_stat E_task(J)
------ ----- ------ ------- ------- ---------
800 0.70 3.750 1.764 3.563 5.327
1600 0.85 1.875 2.601 1.875 4.476
3000 1.12 1.000 4.516 1.240 5.756 N/f and dynamic power goes as V^2 f, so E_dyn = (k V^2 f)(N/f) = k V^2 N and the f cancels. What is left is voltage, and the operating-point table raises voltage from 0.70 V to 1.12 V to sustain the higher clock. The tempting wrong answer is the last one, but shorter run time is what makes E_stat fall down the column, not what makes E_dyn rise. Those two opposing terms are the whole reason the sum has a minimum at 1600 MHz. --platform 3.0. The knee climbs from 1600 MHz to 2400 MHz, and the penalty for crawling at 800 MHz grows from 19% to 72%. Now consider a wearable: tiny platform power, and a genuinely deep idle state. Where should its knee sit relative to a phone's?V^2 term punishing speed and the per-second term punishing slowness balance. Shrink the per-second term and the balance point moves down, so a wearable should clock conservatively rather than race. The tempting wrong answer is the second one: the knee is not a property of the core - it is a property of the whole platform, which is the entire point of that rep. Running the same model with a hard deadline shows a third answer, where the window rather than energy picks the point. delta_T / R_thermal, and mass appears in neither term - so the steady state is unmoved. The tempting wrong answer is the first, which quietly assumes storing heat is the same as removing it; only lowering thermal resistance or raising the allowed temperature rise does that. This is why the chapter calls thermal mass the most instructive knob on the panel.