Appendix A

The Mobile Architecture Workbench

Every tool this course needs, standing up in one sitting on any laptop — with a browser-only path for a locked-down machine and an optional route for students who own an Android device

Appendix A — The Mobile Architecture Workbench

This course measures things. Not in Week 6, not once, not as a garnish on an essay — every week, on your own machine, with a repetition count and a dispersion figure. Three of The Four Questions — performance, energy, thermals — are answered with numbers you take yourself, and the fourth, placement, is decided from them. That means the bench has to be standing before the first lab, and it means the bench has to be trustworthy, which is a harder and much less discussed requirement. A compiler that runs is not a workbench. A compiler that runs on a laptop quietly rescaling its own clocks halfway through your benchmark is a machine for generating confident, reproducible, wrong numbers.

So this appendix does two jobs. Most of it gets you from nothing to a working toolchain in one sitting. The last part — A.8 — is the one almost nobody writes down, and it separates a graduate measurement from an undergraduate one: two runs that prove your bench is measuring what you think it is measuring.

There are three workbenches, and the names are used throughout the book exactly as defined here.

NameWhat it isStatus
LWorkbench LA laptop you control, with a C++17 compiler and Python 3The default if you have admin rights
BWorkbench BA cloud development environment in a browser tab. Nothing installed, no administrator rights, no deviceA first-class citizen, not a consolation prize
DWorkbench DWorkbench L plus an Android device with developer options and the debug bridgeAlways optional. Never required by anything graded at Normal tier

Read A.1, pick one, build it in the sitting described there, then run the calibration pass in A.8 before you write down your first number.

Two things run through every section.

Every version, allowance, menu path, and package name below is a snapshot, not a law. Operating systems rename their settings, cloud vendors change their tiers, and phone manufacturers move developer options somewhere new every couple of releases. Where this appendix cannot be certain, it tells you the shape of the thing and sends you to the vendor’s own documentation — the same discipline the course grades in every report you write.

Do the setup yourself. Asking an assistant to explain a linker error is a good use of it. Pasting a setup script you do not understand into a terminal is not setup. And never take a package name, a flag, a free-tier limit, or a menu path from a model without checking it — those are exactly the facts a language model invents most fluently, in exactly the tone of a manual.


A.1 — The Decision Table: Which Workbench Is Yours

Be honest about the machine and the permissions you actually have, not the ones you wish you had. Find your row.

What you actually haveTakeWhere to go
A school- or work-managed laptop with no administrator rights; a Chromebook; a borrowed, shared, or library machineWorkbench BA.4
A macOS machine you own and can install onWorkbench LA.3.1
A Linux machine you own and can install onWorkbench LA.3.2
A Windows machine you own, with rights to enable the Windows Subsystem for LinuxWorkbench L via WSLA.3.3
A Windows machine you own but cannot enable WSL on (policy, virtualization disabled in firmware, an edition without it)Try the native compiler route; if that fails, Workbench BA.3.3
A very old or low-memory machine you ownWorkbench B, and do not fight itA.4
Any of the above plus an Android phone or tablet you ownYour workbench from above, plus Workbench DA.5
Any of the above plus an iPhone or iPadYour workbench from above. The phone is not a workbench in this courseA.5
Admin rights at home, none on the machine you use between classesBuild both. Workbench L at home, Workbench B for everywhere elseA.3 and A.4
You are not sure what rights you haveOpen a terminal and run sudo -v (macOS/Linux). If it refuses or you have no password, you do not have admin. Take Workbench BA.4

The guarantee

Read this once and then stop worrying about it:

Every Normal-tier requirement in this book is completable on Workbench B alone. No lab, no exam, no quiz, and no part of the capstone requires a phone, a power meter, an oscilloscope, a paid tool, a GPU, or administrator rights on anything. A student working on a locked-down Chromebook with an iPhone in their pocket can earn 100 in this course.

That is a design constraint on the book, not a courtesy. The two places where a physical device would genuinely tell you something more — a real thermal soak on mobile silicon in Lab 5, a system trace in Lab 6 — are marked optional, sit in the Medium or Hard tiers, and carry a complete non-device path worth identical points. A.4 lists every one of them by name so you can verify the claim rather than take it on faith.

The full weighting is in Appendix D and it does not change:

ComponentWeight
Six weekly labs (Weeks 1–3, 5–7), 7% each42%
Week 4 Placement Practical8%
Eight weekly quizzes, 1.5% each12%
Week 4 auto-graded checkpoint (cumulative, Weeks 1–4)10%
Capstone SoC Investigation20%
Capstone Technical Briefing (12 min, recorded)8%
Total100%

Nothing in that table has a hardware prerequisite beyond a browser.

The sitting

Do it in this order. Each row assumes the one above it exists.

#What you are doingMinutes
1Read A.1, find your row, commit to a workbench5
2Install the toolchain (A.3) or stand up the cloud environment (A.4)20
3Run the two --version checks and read the output properly5
4The Week-1 acceptance test: compile one program, run one script (A.3.4)10
5Type calibrate.cpp from A.8 and build it15
6Run calibration Pass 1 and read the verdict2
7Run calibration Pass 2 for sixty seconds and read the verdict3
8Paste both verdict lines into your measurements.xlsx with today’s date5
9Optional: enable developer options on a device you own (A.5)10
Total (without row 9)65

One hour. If you find yourself in hour three, you have almost certainly wandered onto Workbench L without the rights to finish it — go back to A.1, take the Workbench B row, and lose nothing.

Coach’s Note — Do not skip rows 5 through 8 because they look like extras. They are the only rows in this table that will change a grade. Rows 1–4 tell you the compiler exists; rows 5–8 tell you whether the compiler is attached to a machine that holds still. The single most common cause of a wrong student result is never a wrong program. It is a right program run on a laptop that was on battery, thermally soaked, or stepping between operating points — and the student had no way to know, because nobody had told them to look.


A.2 — What This Course Actually Requires of a Machine

Four things. That is the whole list.

PieceWhy the course needs itUsed from
A C++17 compilerg++ or clang++Every measurement program. Compiled, for the reasons in A.6Week 1
Python 3 — standard library onlyEvery analysis and model programWeek 1
A text editor and a shellTyping programs, running them, reading CSVWeek 1
A browser and somewhere to publish a repositoryVendor specification pages, the interactive labs, submissionWeek 1

What you do not need, at any point, for any tier: Android Studio. The Android SDK or NDK. A phone. A tablet. A GPU. numpy, pandas, matplotlib, or any third-party package whatsoever — every plot in this book is ASCII, printed by a program using nothing but the standard library, precisely so it renders on a locked-down machine. A paid editor, a paid profiler, a paid cloud plan, a license for anything. An oscilloscope, a power meter, a USB current monitor. Administrator rights on the machine you are reading this on.

If somebody tells you a graduate architecture course requires you to buy hardware, hand them this section.

Every file you will run, and what runs it

This is the complete inventory. Nothing outside it is needed, and the right-hand column is the only dependency any of it has.

WeekChapter’s code/ filesRuns under
1soc-blocks.csv, block_budget.py, isa_compare.cpp, regwalk.sPython 3 · C++17 · (regwalk.s is a reading exercise — nothing assembles it)
2core-profiles.csv, placement_model.py, amdahl.py, migrate.cppPython 3 · C++17 with -pthread
3cache_walk.cpp, pointer_chase.cpp, matmul_order.cpp, hierarchy_plot.py, model-memory.csvC++17 · Python 3
4placement-bench.csv, partition.py, quantize.py, tile_cost.cppPython 3 · C++17
5thermal-soak.csv, throttle_analysis.py, energy_model.cpp, battery.pyPython 3 · C++17
6bus-traffic.csv, qos_arbiter.py, irq_vs_poll.cpp, radio_energy.pyPython 3 · C++17
7boot-chain.csv, chain_check.py, pqc_budget.py, pqc-sizes-example.csv, timing_leak.cppPython 3 · C++17
8bench_harness.cpp, counters.py, compare.py, soc-compare.csv, sample-perf-stat.txtC++17 · Python 3

Plus the files you type yourself in the reps — the reps say “type it, do not paste it,” and they mean it: sumfn.cpp and branch.cpp (Ch 1, Reps 6 and 8), aos_soa.cpp (Ch 3, Rep 5), soak.py (Ch 5, Rep 8 — Python on purpose, see A.6), nobar.cpp (Ch 8, Rep 2), and calibrate.cpp from this appendix. Same two dependencies; nothing new.

The build line

One canonical form, used everywhere in the book:

g++ -O2 -std=c++17 -o <program> code/<program>.cpp

Two exceptions, both of which will bite you if you do not know them now:

FileThe lineWhy
migrate.cppg++ -O2 -std=c++17 -pthread migrate.cpp -o migrateIt spawns threads. Without -pthread some toolchains link but throw std::system_error at run time
matmul_order.cppBuild at -O2, not -O3At -O3 some compilers interchange the loops for you and erase the very effect you are measuring. Try both and report what yours did — that is Rep 4

And one rule with no exception: never -O0. At -O0 you are timing the compiler’s un-optimized scaffolding — register spills, unelided temporaries, a function call for everything — not the machine. Chapter 8 Rep 2 makes you build at -O0 deliberately, once, so you can see how badly it misleads, and then never again.

If your compiler is clang++ rather than g++, substitute the name and change nothing else. Every flag above means the same thing to both.


A.3 — Workbench L: The Local Path

Faster than the browser, works on a plane, consumes nobody’s allowance, and gives you the operating-system interfaces that make Chapter 5 richer. It needs administrator rights. If you cannot get them, take Workbench B (A.4) and stop negotiating with your IT desk — a week spent on that is an eighth of this course, and you lose nothing graded.

A.3.1 — macOS

You do not need Xcode. You need the command line developer tools, which are a fraction of the size and install from the terminal:

xcode-select --install

A dialog appears; accept it and wait. If it reports the tools are already installed, they are, and you are done. Python 3 arrives with the same package on current macOS versions — verify rather than assume, and if python3 is missing, install a current Python 3 from python.org.

One thing to understand before it confuses you. On macOS, g++ is not GNU g++. It is a wrapper that invokes Apple’s clang, which is why g++ --version prints something with the word clang in it. That is correct and expected — every program in this book is standard C++17 and builds identically under either. When a rep says g++, type g++.

The second thing matters in Week 1 Rep 6: Apple’s assembler prints vector lane shapes on the mnemonic (saddw.2d v1, v1, v16) where the GNU assembler prints them on the operand (add v1.4s, v1.4s, v2.4s). Both mean the same thing, and a student grepping for v0.4s on a Mac concludes “not vectorized” about code that plainly is.

A.3.2 — Linux

Pick the line for your distribution family. All of them want the same two things: a C++ compiler and Python 3.

FamilyCommand
Debian, Ubuntu, and derivativessudo apt update && sudo apt install -y build-essential python3
Fedora, RHEL, and derivativessudo dnf install -y gcc-c++ python3
Arch and derivativessudo pacman -S --needed base-devel python
openSUSEsudo zypper install -y gcc-c++ python3
Alpinesudo apk add build-base python3

build-essential and base-devel are meta-packages that pull in the compiler, the linker, and the standard headers together, which is why they are preferred over installing g++ alone.

Linux is the richest workbench for this course, and not because of the compiler. It is the only mainstream platform that exposes the operating system’s own power and frequency machinery as ordinary files:

PathWhat it tells you
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governorWhich governor is deciding your clocks right now
.../cpufreq/scaling_available_frequenciesThe operating-point menu the hardware offers
/sys/devices/system/cpu/cpu0/cpuidle/state*/The idle-state ladder: exit latency, required residency, use count
/sys/class/power_supply/BAT*/power_nowInstantaneous draw, in microwatts, if the machine has a battery
/sys/class/thermal/thermal_zone*/tempWhatever thermal zones the platform chose to expose

Chapter 5 Rep 3 walks all of these, and A.8 uses the first. Several are absent inside containers and virtual machines — that absence is a finding you report, not a failure to work around.

A.3.3 — Windows

Two routes that work and one that does not.

Route 1 — the Windows Subsystem for Linux. This is the recommended path. You get a real Linux userspace, the exact commands printed in every rep, a POSIX shell for the loops the reps use, and — for the /sys interfaces above — at least a Linux-shaped view of the world. Enable it from an administrator PowerShell:

wsl --install

Reboot, let the default distribution finish its first-run setup, then open the Linux shell and follow A.3.2 exactly as written. The command name, the default distribution, and the exact prerequisites vary by Windows version and edition, and hardware virtualization has to be enabled in firmware — if wsl --install fails, read Microsoft’s own documentation for your version rather than guessing. Some managed machines have this disabled by policy.

Route 2 — a native Windows toolchain. If WSL is unavailable but you can still install software, a MinGW-w64 distribution (MSYS2 is the common way to get one) gives you a real g++ on Windows, and Python 3 installs from python.org. Every program in the book compiles under it. Two friction points: the reps’ shell loops (for s in 64 128 256; do ...; done) are POSIX shell and need Git Bash or the MSYS2 shell rather than PowerShell, and Windows path separators occasionally surprise a redirect.

The route that does not work: Microsoft Visual C++ (cl.exe) as a drop-in. The compiler is fine; the command lines in this book are not portable to it. -O2 -std=c++17 -o foo is GCC/Clang syntax, and every rep, project, and troubleshooting entry here assumes it. Choosing MSVC means translating flags yourself for eight weeks, unsupported, for no gain.

No administrator rights on the Windows machine? Take Workbench B, A.4. It is one browser tab and it is genuinely fine.

A.3.4 — Verify Workbench L

Three checks, in this order. Do not skip to the third.

Check 1 — the compiler answers.

g++ --version

Expected shape on Linux (GNU):

g++ (<packager's build string>) <major>.<minor>.<patch>
Copyright (C) <year> Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Expected shape on macOS (Apple clang wearing the name):

Apple clang version <version> (clang-<build>)
Target: arm64-apple-darwin<version>          <- or x86_64-apple-darwin<version>
Thread model: posix
InstalledDir: <an Xcode or CommandLineTools path>

What you are checking: a version line appears at all, and the Target: triple (on clang) names an architecture you recognize. Write that architecture down — arm64 or x86_64 — because Chapter 1 Rep 6 has you read your compiler’s assembly output, and which instruction set you are reading is the point of that rep.

Check 2 — Python answers.

python3 --version

Expected shape: Python 3.<minor>.<patch>. Any Python 3 currently shipping with a supported operating system is fine; the programs use formatted string literals and nothing more exotic, and they import only sys, os, csv, re, random, and argparse — all standard library. If the command is not found but python --version reports a 3.x, use python everywhere the book says python3; if it reports a 2.x, install Python 3.

Check 3 — the acceptance test. This is the one that counts, because it exercises the compiler, the linker, the standard library, the shell, and Python in one pass. Run it from Chapter 1’s directory:

g++ -O2 -std=c++17 -o isa_compare code/isa_compare.cpp && ./isa_compare
python3 code/block_budget.py

The first command should print a small table: a header naming the array size and repetition count, then one row per spelling of the kernel — scalar, unrolled x4, lane block x4 — each with a median in seconds, a throughput figure, a ratio against scalar, and a result value. The numbers are yours and will not match anyone else’s. What proves the bench works is the shape: three rows, three medians, three ratios, and a note at the bottom about the last digits of the three results differing.

The second command should print a bar chart of die-area share by block, a rollup by category, and a closing block answering how much of the chip is not the CPU.

If both worked, Workbench L is real. Go to A.6 and then to the calibration pass in A.8. If either failed, A.10 almost certainly has your symptom.


A.4 — Workbench B: The Browser Path (a first-class citizen)

Nothing installed. No administrator rights. No download. One browser tab on a machine you do not own, and every graded requirement in this book within reach.

Be precise about the word “first-class,” because students read a browser path as a fallback and then quietly under-invest in it. Workbench B is not a reduced version of the course. It is the same course, with two capabilities removed that affect a handful of clearly marked optional steps — and those removals are, awkwardly for the consolation-prize reading, also a teaching opportunity, because “I could not measure this, and here is exactly what I would have needed” is a sentence Chapter 8 grades you on writing well.

What it is, generically

A cloud development environment is a Linux container running somewhere else, with a full editor and a real terminal delivered to your browser. You are the administrator inside the container; no rights on your laptop are involved at any point. You reach it from a repository, it starts in about a minute, and it dies when you delete it.

The default recommendation is the cloud development environment built into GitHub, because it launches from the repository you are submitting anyway and needs nothing but a free account. Alternatives that also work: a browser IDE that executes real code in a tab, a hosted notebook service if you are comfortable driving g++ from a shell cell, or a small cloud virtual machine reached through the vendor’s browser-based shell.

This book deliberately prints no price, free-tier size, or monthly allowance for any of them. Those change without notice and cannot be verified for the day you read this. Read the provider’s own current documentation, write the number down with the date you checked it, and treat it as a fact with an expiry — the same habit every lab in this course grades.

Two things bite students on every provider:

  • Storage is usually billed on stopped environments, not just running ones. Delete an environment you are finished with; do not merely stop it.
  • The container’s disk is not backup. Commit and push at the end of every session. Anything uncommitted can vanish when the environment is reclaimed.

Standing it up

Create a free account and a repository for this course, using an address that survives graduation. Launch a development environment from that repository and wait for the terminal. Run the verification below. Commit one line to a README and confirm it appears in the repository from a browser tab. Then delete the environment — you now know the path works, on today’s network, from today’s machine, which you want to have learned in Week 1 rather than Week 5.

Verify Workbench B

Exactly the same three checks as Workbench L, run in the browser terminal. That is the point: the commands do not change, so the book does not need two sets of instructions.

g++ --version
python3 --version

Expected shape: the GNU form shown in A.3.4 for the compiler, and Python 3.<minor>.<patch> for Python. Most cloud images ship both preinstalled. If the compiler is missing, you are root inside the container, so install it with the Debian/Ubuntu line from A.3.2 — a permitted sudo inside a container you own, which is not the same thing as needing admin on your laptop.

Then the acceptance test, unchanged:

g++ -O2 -std=c++17 -o isa_compare code/isa_compare.cpp && ./isa_compare
python3 code/block_budget.py

Same expected shapes. If both work, go do the calibration pass in A.8 — which matters more on Workbench B than on Workbench L, because a shared host is exactly the environment where a benchmark drifts for reasons that have nothing to do with your program.

What Workbench B cannot do

Two capabilities, stated plainly.

1. It cannot give you a thermal environment you own. You are on server-class hardware in a data centre, sharing it with tenants you cannot see. Three consequences:

  • The host may have real cooling and never throttle at all, so Chapter 5’s sustained-load run may show a flat line where a laptop shows a falling one.
  • Or it may drift — and you cannot tell whether that drift was heat, a scheduler, or a neighbour compiling something enormous on the next core.
  • The thermal transient the course cares about — a device absorbing a burst it cannot sustain because the chassis has heat capacity — is a property of a small sealed object in your hand. A data-centre host does not have it.

2. It cannot give you physical-device counters or platform power. No hardware performance counters (unprivileged access to the performance monitoring unit is normally unavailable inside a container, and often absent in the virtual machine underneath it). No adb, no device. No battery, so no /sys/class/power_supply. Frequently a truncated cpufreq and cpuidle view, or none at all.

Exactly which steps that affects — and their tier

Here is the complete list. Check it against the labs yourself; that is why it is a table.

WhereThe stepTierWhat you do instead
Ch 5, Rep 3Read the governor, the frequency menu, and the idle-state ladderRep (ungraded)You may see one governor and a truncated ladder. The rep already tells you that is a finding — report what you see and say why it is truncated
Ch 5, Rep 4Convert seconds into joules using a platform power figureRep (ungraded)Report the time, state that power was not measurable, and use time as a stated relative energy proxy at fixed configuration — Chapter 8 §8.7 licenses exactly this
Ch 5, Rep 8A ten-minute soak on your own machineRep (ungraded)Runs fine. If nothing throttles, that is a legitimate result — say so and give the two most likely reasons
Lab 5, Part 4A real sustained-load run of ten minutes or moreNormalRuns on Workbench B. The rubric explicitly credits “a negative result handled correctly.” No throttling is an answer, if you say why
Ch 5, Rep 10adb shell dumpsys thermalserviceRep, marked optionalSkip. The rep’s graded reasoning is about the headroom ladder, which is on the page
Ch 8, Rep 6Produce your own perf stat counter setRep (ungraded)Use the shipped sample-perf-stat.txt, which exists for this reason and is labeled synthetic on its first line
Lab 8 (capstone)Counter analysisNormalThe capstone’s required measurements are timing measurements. Counter analysis may be done on the supplied sample with the limitation stated — Chapter 8 §8.8 says so in as many words
Lab 2Pin a thread to a specific coreMediummigrate.cpp has a portable fallback and reports the cold-cache cost without pinning. On Linux hosts pinning often works anyway — try it
Lab 2, optional pathDevice battery-usage attributionOptional, zero additional pointsSkip
Lab 5, H2Device thermal captureHard, and marked “never required”H1 is the graded Hard piece and is scored identically without H2
Lab 6, optionalA system trace on a deviceOptional bonus, any tierSkip

Read down the tier column. Two rows say Normal, and both run on Workbench B — one because a negative result is explicitly creditable, the other because the book says outright that counter analysis on the supplied sample is acceptable with the limitation stated. Everything else is a rep, an optional step, or an extra-credit tier. No Normal-tier requirement anywhere in this book depends on a capability Workbench B lacks. The guarantee in A.1 holds.

The one thing you must do that Workbench L students need not

Say that you were on Workbench B, every time. A shared cloud host is a legitimate measurement condition and naming it is worth points — Lab 3 says so explicitly, and the conditions block in Appendix C has a field for it. Expect wider dispersion than a quiet laptop. Report the median with its interquartile range, note the host was shared, and you have written a better measurement than a Workbench L student who reported one clean number without conditions.

Coach’s Note — The browser student’s disadvantage is smaller than they think and their advantage is bigger than they think. A cloud environment is defined by a file in your repository, so a grader can reproduce it exactly — your numbers are checkable in a way a laptop’s never are. In Week 8, when the capstone asks what a stranger would need to reproduce your measurement, the honest answer for most laptops is “my laptop, in the state it was in that afternoon.” Yours is a container definition. That is not a consolation. That is the better answer.


A.5 — Workbench D: The Optional Device

Skipping this section costs you nothing. Not a point, not a tier, not a rubric line. Read that again if you were about to buy a used phone for this course: do not. Every graded item in the book has a complete, identically-scored path that does not touch a device, and A.4 lists every device step by name and tier so you can check.

Workbench D is for the student who already owns an Android phone or tablet and wants to see the platform they have been reasoning about actually answer. There is a real difference between reading that the thermal ladder exists and watching THERMAL_STATUS_LIGHT appear on a device warming up in your hand. That difference is worth an hour if you have the hardware, and nothing at all if you have to acquire it.

Only do this on a device you own. Not a work phone, not a family member’s, not a device under a management policy — enabling developer options there may violate a policy you signed, and the debug bridge is a genuinely powerful interface.

An iPhone or iPad is not Workbench D. Apple’s platform does not offer an equivalent path for this course’s purposes, and nothing in the book asks for one. If your phone is an iPhone you are on Workbench L or B, like most of the class, with no penalty whatsoever.

Enabling it, in the general shape

Menu paths vary by manufacturer, by Android version, and sometimes by carrier build. This book will not print a specific sequence and claim it is yours — that is precisely the kind of confident, plausible, wrong instruction the course spends eight weeks teaching you to distrust. The shape is stable even when the path is not:

  1. Find the build-number entry in your device’s settings, usually somewhere under an “About phone” or “About device” screen, occasionally one level deeper under a “Software information” sub-screen.
  2. Tap it repeatedly until the device tells you developer options are enabled. It will count down at you.
  3. Open the new developer options screen, which appears somewhere in settings — often under a system or additional-settings group.
  4. Enable USB debugging there.
  5. Connect the device by cable and accept the authorization prompt on the device screen. The prompt appears once per computer; if it never appears, the cable is charge-only, which is by far the most common cause.

For the current, vendor-neutral version of that sequence — and for everything else in this section — go to https://developer.android.com/ and search for developer options and the Android Debug Bridge. Read the platform’s own page, not a forum post and not a model’s summary of one.

The debug bridge

adb is the Android Debug Bridge, part of the platform tools. Install it from Android’s own distribution rather than from a package that repackages it. Then the only two commands the book actually uses:

adb devices                              # is the device visible and authorized?
adb shell dumpsys thermalservice         # the platform's own thermal view

adb devices should list one device with the word device beside it. If it says unauthorized, look at the phone — the authorization dialog is waiting. If it lists nothing, try another cable before you try anything else.

What Workbench D unlocks, week by week

WeekStepTierWhat it addsPoints if you skip
2Battery-usage attribution across foreground scrolling and background sync, compared to your modelOptional pathReal attribution data against a synthetic model — the gap is instructiveNone lost
5adb shell dumpsys thermalservice before, during, and after a sustained loadRep, optionalWatching the status ladder actually climbNone lost
5Lab 5 H2 — a device soak compared against thermal-soak.csvHard, marked “never required”One place where the modeled curve and a real one disagree, and whyNone lost — H1 is scored identically
6A system trace during a real use case, to check one claim on your integration mapOptional bonusOne claim on your map checked against the platform’s own traceNone lost
8simpleperf stat on a debuggable build, parsed with counters.pyOptionalCounters from mobile silicon rather than a laptopNone lost

Every row says the same thing in the last column. That is not an accident.

Three rules if you do use it

  1. One device, one set of conditions, one measurement. A number off your phone is a fact about your unit, in your case, at your ambient temperature, at that charge level — not about that product, as Lab 5 says explicitly. Log the device, the ambient temperature, whether it was charging, whether it was in a case, and how long you soaked it, or the number is not usable.
  2. Turn USB debugging off when you are done. It is a standing interface into your device.
  3. A device result never replaces the non-device analysis. It is an addition. Every rubric is written that way, so a Workbench D student who skips the modeled work to show a phone trace scores lower, not higher.

A.6 — Why the Measurement Code Is Compiled

Students ask this in Week 1 every time, and it deserves a real answer rather than “because C++ is fast.”

The effects this course measures are small in absolute time. A first-level cache hit and a DRAM access differ by roughly two orders of magnitude in cycles — Chapter 3 hangs an entire week on your ability to see that staircase on your own machine. A branch misprediction costs on the order of the pipeline depth. A cold cache after a migration costs a handful of misses. These are events measured in single-digit to triple-digit nanoseconds, and to see them your measurement apparatus has to cost much less than the thing being measured.

An interpreted language puts its own machinery in between, in four ways, every one of them fatal to the specific experiments in this book:

1. The per-element overhead swamps the signal. In a mainstream Python implementation, acc += data[i] is not one load and one add. It is bytecode dispatch through an interpreter loop, a bounds check, an object unbox, arithmetic on boxed values, an allocation for the result, and reference-count updates — dozens to hundreds of machine instructions with their own dependent memory accesses. If the difference you want to observe is 3 ns against 100 ns and your instrument costs several hundred nanoseconds per element, your instrument is the measurement. The staircase does not shrink; it flattens into a line.

2. The interpreter’s working set competes with yours for the cache. Chapter 3’s experiments work by controlling working-set size precisely — 4 KiB, then 8, then 16, until the data stops fitting in a level and the cost steps up. That control is meaningless if a large interpreter, its bytecode, its object tables, and its allocator share the cache with you and evict your array between iterations. You would be measuring the interpreter’s locality.

3. The data layout is not the layout you wrote. A Python list of floats is an array of pointers to heap-allocated objects. Walking it sequentially in source is a pointer chase in hardware — the exact phenomenon pointer_chase.cpp exists to isolate separately. You cannot demonstrate the difference between sequential access and pointer chasing in a language where sequential access already is pointer chasing. Rep 5’s array-of-structs against struct-of-arrays makes the same point in miniature: the layout is the experiment.

4. You do not control when the runtime does work. Allocation, garbage collection, and dynamic dispatch inject non-determinism inside your timed region. Chapter 8 grades you on dispersion, and a distribution whose tail is somebody else’s garbage collector is not a distribution about your program.

Compiled C++17 removes all four. std::vector<float> is a contiguous array of raw floats; a for loop over it is loads and adds; the timed region contains what you put in it and nothing else. That is what makes a 4 KiB-to-64 MiB sweep produce a readable staircase on an ordinary laptop.

The exception that proves the rule

Notice what the book does not do. Chapter 5 Rep 8 — the ten-minute sustained-load soak — is Python, deliberately, and the rep even tells you to calibrate its chunk size because interpreter speed varies between versions.

That is fine because the quantity being measured is different in scale and in kind. You are not timing a nanosecond-scale memory event; you are asking whether delivered throughput over ten minutes falls as the machine heats up. Interpreter overhead is a large constant factor, and a constant factor cancels out of a ratio — opening rate against closing rate is unaffected by how slow each chunk was in absolute terms. Python is also practical there: no build step, and one copy per core from a shell loop on any machine in the class.

So the rule, stated properly: compile when the measurement’s resolution requirement is near the cost of the instrument; interpret when it is not. Every .cpp file in this book takes a measurement at hardware timescales or evaluates a model whose arithmetic is exact. Every .py file reads data somebody else produced, evaluates a model, or drives a long-timescale experiment. Learn to make that call yourself — it is the same judgment as choosing between a microbenchmark and an application benchmark.

One more thing the compiler will do to you

A good optimizer will notice you never used the result of your benchmark loop and delete the loop. Your kernel then runs in zero time and your speedup is infinite. This is the single most common way a student microbenchmark lies, and it is why bench_harness.cpp ships with a do_not_optimize barrier and why Chapter 8 Rep 2 makes you delete that barrier on purpose and watch the numbers become impossible. Any program you write for this course either consumes its result or passes it through a barrier. There is no third option, and “the number looked great” is not evidence that there is.


A.7 — Timing and Counters, Platform by Platform

What you have everywhere

Portable C++ std::chrono::steady_clock is the floor of this course, and a higher floor than students expect. It gives you monotonic elapsed time for a region you delimit, a time_point with nanosecond-representable resolution on mainstream implementations, a clock that never jumps backwards or gets adjusted by a time service (unlike system_clock), and identical semantics on every platform here with no #ifdef. It does not give you any statement about why the time was spent, any per-core or per-block attribution, any energy figure, or a guarantee that the hardware resolves nanoseconds — that last is exactly what A.8 Pass 1 measures.

Everything the capstone requires you to measure is a timing measurement. That is deliberate, and it is what makes the Workbench B guarantee possible. Counters make an analysis richer; they never make it possible.

What each platform adds

PlatformToolWhat it addsThe honest caveat
Linuxperf stat, perf recordReal hardware counters: cycles, instructions, cache misses per level, TLB misses, branch misses, stalls. IPC and MPKI come straight out of itUnprivileged access is restricted by a kernel setting and usually needs an administrator to relax. Counters are frequently absent inside containers and virtual machines regardless of privilege
Linux/sys/devices/system/cpu/...Governor, operating-point menu, idle-state ladder, sometimes battery draw and thermal zonesAlso often absent or truncated in a container
macOSInstruments, and its driver xctraceThe supported profiling path on Apple platforms, including counter-based instrumentsThe output format differs from perf’s, so counters.py will not parse it. Enter the numbers by hand rather than pretending you ran something you did not. Documentation: https://developer.apple.com/
macOSsudo powermetrics --samplers cpu_powerA package-level power and frequency estimate while you measureNeeds administrator rights. It is a whole-package figure; it does not attribute watts to a block
WindowsThe Windows Performance Toolkit (WPR/WPA) and vendor profilersOS-level tracing and sampled profilingDirect architectural-counter access on Windows varies by CPU vendor, driver, virtualization state, and OS build. Verify rather than assume. Under WSL you inherit the host’s power management and a virtualized counter path — treat Windows as a timing-only platform for this course unless you have proven otherwise on your machine
Android (Workbench D)simpleperf, Perfetto, Android Studio Profiler, Android GPU Inspector, the Jetpack benchmark librariesCounters on mobile silicon, system-wide traces, GPU frame analysisNeeds a debuggable build for some paths. https://developer.android.com/ and https://perfetto.dev/
Arm targetsArm Performance Studio — Streamline, Mali Offline Compiler, Frame Advisor, Performance AdvisorThe vendor’s own system profiler and GPU toolinghttps://developer.arm.com/

Where counters are simply unavailable — and what to do instead

On Workbench B you will not have them. On macOS you have a different tool with a different format. On many managed Linux machines the kernel setting is locked. This is normal, and it is planned for.

First: use the sample. sample-perf-stat.txt ships in Chapter 8’s code/ folder, contains three complete runs, is labeled synthetic on its first line, and is parsed by counters.py. Every counter rep in the book works against it. That was designed in, not bolted on.

Second — the professional move — substitute a differential experiment for a counter. When you cannot count an event, construct two runs that differ in nothing but that event and time both. The whole book is built on this technique:

You cannot countSo instead you runThe difference isolates
Cache missescache_walk.cpp at a working set inside a level and just outside itThe capacity boundary of that level
Cache-line utilizationStride 64 against stride 128, same total accessesYour machine’s line size
Prefetcher effectivenesscache_walk.cpp (strided, predictable) against pointer_chase.cpp (random, dependent) at the same working setWhat the prefetcher was buying you — this is the ratio Chapter 3 Rep 3 calls the most examinable idea in the chapter
Branch mispredictionsThe same conditional loop over sorted and unsorted dataThe predictor’s contribution, and nothing else
Migration costThe same kernel pinned and free to move (migrate.cpp), plus a cold-cache controlCache warmth, separated from thread-creation overhead
Instruction-count effectsmatmul_order.cpp in ijk against ikj order — identical multiply-accumulate countPurely the access pattern

Each row is a controlled experiment that yields a number you can defend without a single counter. Then state the limitation: “counters were unavailable on this workbench; the mechanism is inferred from a controlled A/B timing comparison, described in measurements.xlsx.” That sentence is worth more on a rubric than a counter dump nobody interpreted.

Third: never invent the number you could not measure. Not the wattage, not the miss rate, not the cache size. Chapter 8 §8.7 is blunt about the specific failure — multiplying a benchmark score by a plausible-looking wattage you did not measure and calling the quotient performance-per-watt is worse than a guess, because the arithmetic makes it look derived. A fabricated figure is an integrity failure in this course, not a deduction; Appendix D has the policy.


A.8 — The Calibration Pass (the part nobody else writes)

Everything above tells you the bench exists. This section tells you whether it can be trusted, and it takes about five minutes.

The two failures it catches are the two that silently corrupt every subsequent measurement in the course:

  1. Your clock is too coarse or too expensive to see what you are timing. You will get numbers. They will be reproducible. They will be about the clock.
  2. Your machine did not hold one operating point while you measured. A laptop that starts at a boost frequency and falls back thirty seconds later has given you two different machines and one filename. Compare a “before” run to an “after” run on it and you have measured the power management, with every downstream conclusion contaminated.

Neither failure announces itself. Both produce clean-looking output. That is why this pass exists.

The program

Type it into calibrate.cpp at the top of your course folder. Type it; do not paste it. The shape of a timing harness is something your hands should know before Week 8 asks you to build one under time pressure.

// calibrate.cpp — the workbench calibration pass for Mobile Computer Architecture.
// Build: g++ -O2 -std=c++17 -o calibrate calibrate.cpp
// Run:   ./calibrate                 # both passes
//        ./calibrate --seconds 120   # a longer drift pass
//
// Pass 1 asks what your clock can even see. Pass 2 asks whether your machine
// held one operating point while you were measuring. Run it before Week 1's
// first measurement and again the day you start the capstone.

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>

using Clock = std::chrono::steady_clock;
static inline double ns(Clock::duration d) {
    return std::chrono::duration<double, std::nano>(d).count();
}

// A dependent integer chain: one long carried dependency, no memory traffic,
// nothing the optimizer can fold away because the result is printed.
static std::uint64_t kernel(std::uint64_t n, std::uint64_t seed) {
    std::uint64_t x = seed;
    for (std::uint64_t i = 0; i < n; ++i) x = x * 6364136223846793005ULL + 1442695040888963407ULL;
    return x;
}

static double median(std::vector<double> v) {
    if (v.empty()) return 0.0;
    std::sort(v.begin(), v.end());
    const std::size_t m = v.size() / 2;
    return v.size() % 2 ? v[m] : 0.5 * (v[m - 1] + v[m]);
}

int main(int argc, char **argv) {
    double seconds = 60.0;
    for (int i = 1; i < argc - 1; ++i)
        if (!std::strcmp(argv[i], "--seconds")) seconds = std::atof(argv[i + 1]);

    // ---------------- Pass 1: what can the clock see? ----------------
    std::printf("PASS 1 — timer resolution and overhead\n");
    std::printf("  steady_clock is_steady   : %s\n", Clock::is_steady ? "true" : "false");
    std::printf("  steady_clock tick period : %lld/%lld s\n",
                (long long)Clock::period::num, (long long)Clock::period::den);

    // Overhead: the cost of one now() call, measured over a long run of them.
    const int kOver = 200000;
    auto o0 = Clock::now();
    Clock::time_point t;
    for (int i = 0; i < kOver; ++i) t = Clock::now();
    auto o1 = Clock::now();
    (void)t;
    const double overhead_ns = ns(o1 - o0) / kOver;

    // Granularity: the smallest non-zero difference two back-to-back calls report.
    double gran_ns = 1e18;
    for (int trial = 0; trial < 2000; ++trial) {
        auto a = Clock::now(), b = a;
        while (b == a) b = Clock::now();
        gran_ns = std::min(gran_ns, ns(b - a));
    }
    std::printf("  cost of one now() call   : %8.1f ns\n", overhead_ns);
    std::printf("  smallest visible tick    : %8.1f ns\n", gran_ns);
    std::printf("  do not time any region shorter than ~%.0f ns (1000x the call cost)\n",
                overhead_ns * 1000.0);

    // ---------------- Pass 2: did the machine hold still? ----------------
    // Size one work quantum to about 50 ms so each sample is far above the floor above.
    std::uint64_t n = 1 << 16, sink = 12345;
    for (;;) {
        auto a = Clock::now();
        sink ^= kernel(n, sink);
        const double ms = ns(Clock::now() - a) / 1e6;
        if (ms > 50.0 || n > (1ULL << 34)) break;
        n *= 2;
    }

    std::printf("\nPASS 2 — operating-point stability over %.0f s (quantum = %llu iterations)\n",
                seconds, (unsigned long long)n);
    std::vector<double> ms;
    const auto start = Clock::now();
    while (ns(Clock::now() - start) / 1e9 < seconds) {
        auto a = Clock::now();
        sink ^= kernel(n, sink);
        ms.push_back(ns(Clock::now() - a) / 1e6);
    }
    const std::size_t k = ms.size(), fifth = k / 5 ? k / 5 : 1;
    std::vector<double> first(ms.begin(), ms.begin() + fifth), last(ms.end() - fifth, ms.end());
    const double med = median(ms), mf = median(first), ml = median(last);
    std::vector<double> sorted = ms;
    std::sort(sorted.begin(), sorted.end());
    const double iqr = sorted[3 * k / 4] - sorted[k / 4];

    std::printf("  samples                  : %zu\n", k);
    std::printf("  median / min / max  (ms) : %8.2f  %8.2f  %8.2f\n", med,
                *std::min_element(ms.begin(), ms.end()), *std::max_element(ms.begin(), ms.end()));
    std::printf("  interquartile spread     : %8.2f ms  (%.1f%% of median)\n", iqr, 100.0 * iqr / med);
    std::printf("  first fifth / last fifth : %8.2f  %8.2f   ->  drift %+.1f%%\n",
                mf, ml, 100.0 * (ml / mf - 1.0));
    std::printf("  VERDICT: %s\n",
                (ml / mf > 1.10)      ? "CLOCKS FELL during the run — see A.8, do not measure yet"
              : (ml / mf < 0.90)      ? "CLOCKS ROSE during the run — you measured a cold machine"
              : (iqr > 0.05 * med)    ? "NOISY — spread too wide to defend a 10% claim, see A.8"
                                      : "STABLE ENOUGH to measure on");
    std::printf("  (sink %llu — printed so nothing above can be optimized away)\n",
                (unsigned long long)sink);
    return 0;
}

Build and run it:

g++ -O2 -std=c++17 -o calibrate calibrate.cpp
./calibrate

It prints two blocks. Read them in order.

Pass 1 — timer resolution and overhead

The output has this shape (your numbers will differ; the shape is what matters):

PASS 1 — timer resolution and overhead
  steady_clock is_steady   : true
  steady_clock tick period : 1/1000000000 s
  cost of one now() call   :     <n> ns
  smallest visible tick    :     <n> ns
  do not time any region shorter than ~<n> ns (1000x the call cost)

Four things are being reported, and each has a failure mode.

LineGoodBad, and what it meansWhat to do
is_steadytruefalse — the clock can be adjusted or can jump backwardsDo not measure on it. On mainstream implementations this is true; if yours is not, you are on an unusual runtime and should move to another workbench
tick periodA small fraction, typically 1/1000000000 sA coarse periodThis is the representation, not the hardware. The next two lines are the truth
cost of one now() callTens of nanosecondsHundreds of nanoseconds, or into the microsecondsCommon inside some virtual machines where the clock read traps to the hypervisor. Not fatal — it just moves the floor up
smallest visible tickTens of nanosecondsHundreds of nanoseconds, or a microsecond-plus quantumYour clock cannot resolve anything shorter. Every timed region must be far larger than this

The rule this pass gives you, and it is the whole point: never time a region shorter than about a thousand times the cost of a clock read. The program prints that threshold. If your floor comes out at twenty microseconds and you were about to time a single 200-nanosecond kernel iteration, you would have measured the clock and called it a cache effect.

The fix is never “get a better clock.” It is to batch the work inside the timed region: put a loop of many iterations between the two now() calls and divide at the end. Every measurement program in this book already does this — that is what the repetition counts inside cache_walk.cpp, pointer_chase.cpp, and bench_harness.cpp are for. Knowing why they are there is what stops you writing a naive harness in Week 8 and reporting nonsense. If your floor is unusually high, raise the repetition counts the programs expose (--reps, and the loop constants the reps tell you to edit) until each timed region is comfortably above it, and record that you did.

Pass 2 — the power-governor and turbo check

This is the run that catches a laptop silently rescaling its own clocks mid-benchmark. It runs a fixed work quantum over and over for sixty seconds and asks one question: was the last fifth of the run the same speed as the first fifth?

PASS 2 — operating-point stability over 60 s (quantum = <n> iterations)
  samples                  : <n>
  median / min / max  (ms) :    <n>     <n>     <n>
  interquartile spread     :    <n> ms  (<n>% of median)
  first fifth / last fifth :    <n>     <n>   ->  drift <±n>%
  VERDICT: <one of four>

The four verdicts, what each means, and what to do:

VerdictWhat actually happenedWhy it corrupts everything downstream
STABLE ENOUGH to measure onDrift within ±10%, interquartile spread within 5% of the medianNothing. Go measure
CLOCKS FELL during the runThe machine started at a boost operating point and dropped back — thermal limit reached, or a power/turbo budget expiredYour first runs are systematically faster than your last ones. Any A/B comparison run in that order will “prove” that A is faster than B when the only difference was when you ran it
CLOCKS ROSE during the runYou started on a cold, idle, down-clocked machine and the governor ramped upYour warm-up was too short. Your early samples undercount and your medians are polluted by a transient. Chapter 8 Rep 3 is exactly this failure, deliberately induced
NOISYWide spread with no systematic direction — the governor stepping between discrete operating points, or another process (or another tenant) taking the coreYou cannot defend a 10% difference between two builds when your own repeated measurement of the same build spreads wider than 10%

What to do about a bad verdict, by platform:

PlatformActions, in order
Linux (Workbench L)1. Plug the machine in — battery power routinely changes the policy. 2. Read /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor. If it is powersave or an on-demand policy and you have admin, set performance (via cpupower frequency-set -g performance, or by writing to scaling_governor) for the session only. 3. Quit browsers, editors, indexers, and syncing clients. 4. Where your platform exposes a turbo control — on Intel P-state systems, /sys/devices/system/cpu/intel_pstate/no_turbo — you may disable boost to trade peak speed for a flat curve. Not every platform has an equivalent; if yours does not, say so instead of inventing one. 5. Re-run. Put the governor back afterwards — leaving performance pinned on a laptop you use on battery is a real cost you did not intend to pay
macOS (Workbench L)You cannot set a governor; the tunables are not exposed, which is the same philosophy as Chapter 2’s point about Apple refusing to expose CPU affinity and offering quality-of-service classes instead. What you can control: plug in, turn off Low Power Mode, quit background work, let the machine idle before you start, keep it on a hard surface rather than a soft one, and observe with sudo powermetrics --samplers cpu_power while you measure. Then report the condition rather than controlling it
Windows (Workbench L)Plug in and select a high-performance power plan. Under WSL you inherit the host’s power management entirely, so set it on the Windows side, not inside the Linux shell. Then re-run
Workbench BYou have neither the knob nor the visibility, and pretending otherwise is the mistake. Instead: raise repetition counts, report the median with its interquartile range, run your A and B conditions interleaved rather than in blocks so drift affects both equally, and state in measurements.xlsx that the host was shared and drift could not be attributed. That is a complete, professional answer

Two disciplines that apply on every platform, and which cost nothing:

  • Interleave, do not block. If you must compare A against B on a machine that drifts, run A B A B A B rather than AAA BBB. Drift then contaminates both conditions equally instead of masquerading as a difference between them. This one habit rescues more student comparisons than any tuning knob.
  • Re-run the pass, do not re-run until you like it. Running Pass 2 six times and reporting the run that said STABLE is exactly the failure Chapter 8 is about.

What to record

Paste both verdict lines into measurements.xlsx with the date, the machine, and the power state, before your first real measurement of the term. Then do it again at the start of the capstone, because a laptop in Week 8 is not the laptop it was in Week 1.

The conditions block, the median-and-dispersion format, the repetition counts, and the sustained-load soak protocol that all of this feeds into are in Appendix C. This appendix proves the instrument works; Appendix C is the methodology built on top of it. Read them in that order, once, in Week 1.

Coach’s Note — This can sound like fussiness. It is not. A calibration pass is the difference between “my optimization was 12% faster” and “my optimization was 12% faster, on a bench whose repeated measurement of an unchanged binary varies by 2%.” The first is an assertion. The second is evidence. In a design review the person who can say the second sentence wins the argument even as the junior engineer in the room — because everyone else there has been burned by the first one.


A.9 — Milestone → Minimum Workbench

Read this in Week 1, not Week 5. The point of the table is that nobody discovers a missing capability the week it is needed.

WkGraded itemMinimum workbenchWhat Workbench L addsWhat Workbench D adds
1Lab 1 — The SoC Teardown (7%) + the week’s quizB. It is a reading, citation, and reasoning lab; the project says so outrightisa_compare.cpp, and reading your own compiler’s assembly in Rep 6Nothing
2Lab 2 — The Placement Study (7%) + quizBA quieter migrate.cpp measurement; on Linux, real thread pinningAn optional battery-attribution observation — no points
3Lab 3 — The Memory Wall Lab (7%) + quizB. The project explicitly accepts a shared cloud host as a stated conditionA cleaner staircase with lower dispersionNothing
4Placement Practical (8%) + cumulative checkpoint (10%) + the week’s quizB. The practical says every requirement is completable on Workbench B, with no phone and no admin rightsNothing requiredNothing
5Lab 5 — The Energy and Thermal Study (7%) + quizB. Part 4’s real sustained-load run works, and a negative result is explicitly creditableThe governor, the operating-point menu, the idle-state ladder, a battery, and a genuine thermal transientH2’s device thermal capture — Hard tier, marked “never required,” H1 scores identically
6Lab 6 — The Integration Map (7%) + quizBNothing requiredAn optional system trace to check one claim — bonus at any tier
7Lab 7 — The Silicon Threat Model (7%) + quizB. The project states a physical device is never required, at any tierA slightly cleaner timing_leak.cpp signal on a quiet machineNothing
8Capstone SoC Investigation (20%) + Technical Briefing (8%) + quizB, plus something that records your screen and your voice — see belowperf/Instruments counters, if you have accesssimpleperf counters from mobile silicon — optional

Eight weekly quizzes at 1.5% each make up the remaining 12%; the full contract is Appendix D. Every row above costs nothing to satisfy.

The one Week-8 trap

The Technical Briefing is recorded. Twelve minutes plus a three-minute addendum, slides and voice, submitted as a video file or a link to one. It is the only requirement in the course that needs a capability the rest of the course does not, and every term somebody discovers it on the Friday.

Sort it out in Week 1, in ten minutes. Any conferencing tool you already use will record a meeting with one participant — you — sharing a screen, and that needs nothing installed on a locked-down machine. Your operating system very likely has a built-in recorder; browser-based ones exist too. Check that your microphone is actually captured — a silent twelve-minute slide deck is a resubmission. Record thirty seconds of yourself talking over one slide this week and play it back.

What you never need

No GPU. No phone. No paid tool. No hardware purchase of any kind. If a step in this book seems to require one, re-read the tier line — it is optional, and the non-device path is worth the same points.


A.10 — Troubleshooting: The Eight Failures That Will Actually Happen

#Symptom you will actually seeWhat it meansThe fix
1g++: command not found, or macOS says xcrun: error: invalid active developer pathThe command line developer tools are missing or were invalidated by an OS upgrade (which routinely does this); no compiler package on Linux; or you are in PowerShell rather than a Linux shellmacOS: xcode-select --install, and re-run it after every major OS upgrade. Linux: the line for your distribution in A.3.2. Windows: open the WSL shell
2Errors that look like the standard library is missing pieces — 'std::optional' file not found, complaints about structured bindingsYou dropped -std=c++17, or your compiler defaults to an older standardPaste the whole build line every time: g++ -O2 -std=c++17 -o <program> code/<program>.cpp. If the flag is present and it still fails, your compiler predates C++17 — update it, or use Workbench B
3Only migrate misbehaves: undefined reference to pthread_create, or it builds and then dies with std::system_errorThe missing -pthread flag. Some toolchains link without it and fail only at run time, which looks like a bug in the programg++ -O2 -std=c++17 -pthread migrate.cpp -o migrate. It is the only file in the book needing an extra flag, and its own header comment says so
4permission denied on a binary you just built, or ./cache_walk: No such file or directory after a clean buildEither you built into one directory and invoked from another (-o cache_walk writes to the current directory, not code/), or the filesystem is mounted noexec — real on some managed machines and container mountsBuild and run as one line so there is no ambiguity: g++ -O2 -std=c++17 -o cache_walk code/cache_walk.cpp && ./cache_walk --max-mib 64 | head. If it is noexec, build into your home directory
5The cache staircase is flat, or matmul_order shows almost no difference between loop ordersIn order of likelihood: you built at -O0 and are timing the compiler’s scaffolding; you built at -O3 and the compiler interchanged the loops for you; or your working set never left a very large last-level cacheBuild at -O2 — not -O0, not -O3. Push further out with ./cache_walk --max-mib 256. Then record what happened: “my compiler interchanged the loops at -O3, and here is the evidence” is a correct answer to Rep 4, not a failure
6perf prints “Access to performance monitoring and observability operations is limited”, or <not supported> for every eventUnprivileged counter access is restricted by a kernel setting, or the performance monitoring unit is not exposed inside your container or virtual machine. On Workbench B this is normalWith admin on your own machine, relax the kernel setting for the session. Otherwise do not fight it: use sample-perf-stat.txt, get the mechanism evidence from the differential-experiment table in A.7, and state the limitation. That is a complete answer at every tier
7The same program gives numbers 30% apart on consecutive runsAlmost never the program. In order: you are on battery; the machine is thermally soaked from the previous run; you are sharing a host; a browser or indexer woke up; or your timed region is below the timer floorRun A.8 and read the verdict — it usually names the cause. Then plug in, quit background work, raise repetitions, interleave conditions rather than blocking them, and report a median with an interquartile range plus the conditions block from Appendix C
8hierarchy_plot.py or throttle_analysis.py prints nothing, or crashes on a CSV you just madeUsually the redirect: the build silently failed and you re-ran an old binary, or something non-CSV reached standard output. Occasionally you stripped the # comment lines because they looked like clutterhead -n 5 cache-walk.csv before you blame the tool — you should see a # line then a column header. Leave the # lines in. Every loader skips them by design, and the first line of every shipped dataset is its provenance statement

And one that is not a failure at all. pqc_budget.py prints a refusal and reports nothing. That is the program working correctly. It ships knowing no key sizes whatsoever and declines to report until you fill in pqc-sizes-example.csv with sizes you looked up in the primary standard and cited — the project pages at https://csrc.nist.gov/projects/post-quantum-cryptography. A tool that refuses to invent numbers is the tool this course wants you to become.


A.11 — Before You Close This Page

Nine checks. If all of them pass, stop thinking about tooling and go do Chapter 1.

  1. You know which workbench you are on, and you can say why in one sentence.
  2. g++ --version (or clang++ --version) prints a version block, and you know which architecture the target triple names.
  3. python3 --version prints a 3.x.
  4. isa_compare built and printed three rows.
  5. block_budget.py printed a bar chart.
  6. calibrate.cpp built and ran.
  7. Pass 1’s verdict is recorded, and you know your minimum trustworthy timed region in nanoseconds.
  8. Pass 2’s verdict is recorded, with the machine, the power state, and the date.
  9. You have proved you can record your screen and your voice — ten minutes now, or a disaster in Week 8.

Two habits to carry out of this appendix, because they outlive the course.

An uncalibrated bench is worse than no bench. A student with no measurement writes “I could not measure this, and here is what I would need” — honest, and creditable. A student with an uncalibrated bench writes a number, the number is wrong, and nothing in the write-up reveals it. The whole discipline this book is training — The Four Questions asked at once, over hardware you did not design, with every figure either cited or measured — rests on the instrument being trustworthy. Five minutes in Week 1 buys that.

Write down the condition, always. Which workbench, plugged in or not, how long the machine had been idle, which consecutive run, which compiler and flags. Not because a rubric demands it, though several do — because a number without its conditions is an anecdote with a decimal point, and in Week 8 somebody competent and busy is going to ask you measured how, for how long, on what.

Build the bench. Calibrate it. Then leave it alone and go measure something.

See you in Chapter 1.


Up next: Chapter 1 — The Machine in Your Pocket and Lab 1. Learn to read a block diagram and a specification table without being sold to in Appendix B, which also documents the provenance of every dataset in code/. The methodology this appendix’s calibration pass feeds into — repetitions, medians, dispersion, the conditions block, the sustained-load soak protocol, and every report template you are graded against — is Appendix C. The grading contract and the AI-use policy are Appendix D. The vocabulary is Appendix E. The whole eight-week arc is the lab arc.