Chapter 2 — Reps
Conditioning, not grading. This week’s reps put the placement rule into your hands: see the asymmetry on real hardware, compute performance-per-watt from ratios, measure what a cold cache costs, separate two overheads that arrive mixed together, and stop believing a core count.
Ground rules:
- Type every command yourself. No copy-paste from the chapter.
taskset -c 0,sysctl hw.nperflevels, and--hetero 1,3,4have to live in your fingers, because in a design review nobody hands you the textbook. - Run everything. Every rep here executes on any of the three workbenches in Appendix A — including the browser one. Reading about a migration penalty is not the same as watching your own machine pay it.
- Predict before you measure. Every measuring rep: write your prediction first — the direction, and a rough magnitude — then run it. The gap between your prediction and the result is the entire lesson. A rep where you guessed right taught you nothing new; a rep where you guessed wrong just made you a better engineer.
- Report like an adult. Warm-up runs get discarded. Report a median with a dispersion measure, never a single number, and state your conditions — plugged in or not, what else was running, which cores. Appendix C has the template. Start now; Week 8 grades it.
- AI policy — explain, never source. Use a model to explain a concept freely. Do not accept a figure from one. Every number you write down traces to a vendor specification page, an architecture reference manual, or your own measurement. Rep 11 makes you watch the failure happen on purpose. End each AI-touching rep with a one-line AI usage note.
Everything below runs from this chapter’s code/ directory. Build the C++ once and keep the binary:
g++ -O2 -std=c++17 -pthread migrate.cpp -o migrate
Reps 1–3: Seeing the Asymmetry
Rep 1 — Inventory the asymmetry on your own machine
Predict first. Without looking: how many logical CPUs does your machine have? Are they all the same microarchitecture, or is your machine heterogeneous? Write your answer down before you run anything.
Now find out. On Linux:
nproc
lscpu | head -n 20
# arm64 only — normalized per-CPU capacity. Absent on x86.
cat /sys/devices/system/cpu/cpu*/cpu_capacity 2>/dev/null
# Widely available — per-CPU frequency ceilings often reveal the split:
cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq 2>/dev/null
On macOS (Apple silicon is heterogeneous, and the OS will tell you so):
sysctl hw.nperflevels hw.physicalcpu hw.logicalcpu
sysctl hw.perflevel0.name hw.perflevel0.physicalcpu hw.perflevel0.l2cachesize
sysctl hw.perflevel1.name hw.perflevel1.physicalcpu hw.perflevel1.l2cachesize
sysctl hw.cachelinesize
On Windows, Get-CimInstance Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors gets you the count; the asymmetry, if any, is easier to read in Task Manager’s per-core graphs under sustained load.
Write down: the number of logical CPUs, whether your machine is heterogeneous, and — if it is — how many of each class and what the L2 sizes are. Then one sentence: which of these numbers came from the OS itself (a primary source you can re-run) and which you would have had to look up? That distinction is the whole of this course’s evidence policy.
Rep 2 — Hand-compute throughput and performance-per-watt
No tools. Paper and head only. Open code/core-profiles.csv and read the three rows. For each class compute, by hand:
- relative throughput =
relative_ipc × max_freq_ghz - relative performance-per-watt = relative throughput ÷
relative_power_at_max - relative energy per instruction = the reciprocal of the above
Then answer three questions in writing:
- How many times the throughput of an efficiency core does one prime core deliver?
- How many times the energy per instruction does that prime core spend?
- For the silicon area of one prime core (
relative_area5.20), how many efficiency cores could you buy, and what total throughput would they deliver? Is that more or less than the prime core’s?
Now check yourself against §2.9’s table. Where you were wrong, say why. Finally, one sentence: given your answer to question 3, why does anyone build a prime core at all?
Rep 3 — Find the deadline where the answer flips
A workload retires 24 million instructions. Run the model at a generous deadline and at a tight one:
python3 placement_model.py --instructions 24e6 --deadline-ms 50
python3 placement_model.py --instructions 24e6 --deadline-ms 8.3
Predict first: as you tighten the deadline from 50 ms down toward 1 ms, how many times will the recommended core change, and in what order? Write it down.
Now bisect. Run the tool at deadlines of 50, 12, 11, 5, 4.5, 4, 3.5 and 3 ms and record which class it recommends at each. You should find three flip points, and each one sits exactly at some core class’s completion time — which is not a coincidence, it is the rule working.
Write down: the three flip deadlines, the class recommended in each band, and one sentence on why the boundaries land exactly where they do. Then run this and explain the result:
python3 placement_model.py --instructions 1.2e9 --deadline-ms 60000
python3 placement_model.py --instructions 1.2e9 --deadline-ms 60000 --platform-mw 1000
One sentence: what changed, and what does that tell you about the claim “the efficiency core is always the cheapest place to run work”?
Reps 4–6: What Migration Costs
Rep 4 — Measure the migration penalty three ways
Predict first, and be specific about signs. For each of the three gaps the program prints, write down whether you expect it to be positive, negative, or near zero, and roughly how big:
cold − warmmigrating (inner) − warmouter − inner
Now run it:
./migrate --kib 256 --rounds 60
Read the header line first: did the program report that affinity control is available or not available on your platform? That single line determines how much you are allowed to conclude from the migrating rows.
Write down: the four medians with their IQRs, the three gaps, and — the real question — for each gap you predicted wrongly, the mechanism you had not accounted for. Then one sentence on why the cold row is the control experiment and the migrating rows are not.
Rep 5 — Grow the working set until the penalty disappears
Predict first: as the working set grows from 64 KiB to 4 MiB, does the cold ÷ warm ratio get bigger or smaller? Commit to an answer and a reason before you run anything.
for k in 64 128 256 512 1024 2048 4096; do
echo "=== kib=$k ==="
./migrate --kib $k --rounds 40 | grep -E "cache-warmth|warm \(same"
done
Tabulate working-set size against the cold ÷ warm ratio. You should see the ratio at its largest for the small sizes and collapsing toward 1 as the working set grows.
Write down: the table, the size at which the ratio starts to fall off, and how that size compares to the L2 you found in Rep 1. Then the payoff sentence: which threads can a scheduler migrate freely, and which should it leave where they are? Answer it in terms of working sets, not adjectives — that answer is a row in your Project 2 write-up.
Rep 6 — Subtract the thread-creation overhead honestly
The migrating (outer) number is two things added together. Separate them.
./migrate --kib 256 --rounds 200
./migrate --kib 256 --rounds 200 # run it again — is the answer stable?
./migrate --kib 2048 --rounds 100 # a longer round dilutes the spawn cost
For each run, compute:
outer − inner— the cost of creating and joining a thread, which has nothing to do with caches.inner − warm— what arriving on a (possibly) different core cost, which has nothing to do with thread creation.
Write down: both numbers for all three runs, and answer three things. First: is outer − inner roughly constant across the three runs? It should be — it is a fixed per-thread cost. Second: as a share of the round, does it grow or shrink when the round gets longer, and why does that matter for how you design a benchmark? Third — and this is the one Chapter 8 will grade — write the single sentence you would put in a report to state the migration cost you measured, in a way that a reader cannot mistake it for the spawn cost. AI usage: none.
Reps 7–9: Scheduling, Scaling, and the Core-Count Lie
Rep 7 — Affinity on Linux, quality-of-service on Apple
Find out what your platform will let you say.
On Linux, take control and prove it changes the answer:
taskset -c 0 ./migrate --kib 256 --rounds 60 | grep "warm (same"
taskset -c $(($(nproc)-1)) ./migrate --kib 256 --rounds 60 | grep "warm (same"
On a heterogeneous machine those two numbers should differ — sometimes substantially. On macOS, try to do the same thing and observe that you cannot: migrate prints affinity control: NOT available here. Instead read what the platform does offer, and match each of your own workloads to one:
| QoS class | You are asserting |
|---|---|
QOS_CLASS_USER_INTERACTIVE | The user is watching; this is on a frame’s critical path |
QOS_CLASS_USER_INITIATED | The user asked and is waiting for a result |
QOS_CLASS_UTILITY | Long-running, progress shown, user not blocked |
QOS_CLASS_BACKGROUND | The user does not know this is happening |
Write down: for the four workloads named in §2.1 and §2.9 — a scroll frame, a background photo index, a wake-word check, a photo-edit apply — the QoS class you would declare for each. Then the argument, in three sentences: which is the better interface for shipping code and which for measurement code, and why the answer is different for the two jobs.
Rep 8 — Kill “more cores is more fast” with Amdahl
By hand first. Using speedup = 1 / ((1 − p) + p/n), compute the speedup at p = 0.95 for n = 2, 4, 8, and the ceiling at infinite cores. Then check yourself:
python3 amdahl.py
python3 amdahl.py --bar 0.90 --max-cores 8
Write down: your three hand numbers and the tool’s, and where you drifted. Then the two questions that matter. First: at p = 0.90, how many cores do you need to reach 90% of the infinite-core ceiling — and is that a number anyone ships? Second: a colleague says the new part is “twice as fast because it has twice the cores.” Write the one-sentence reply you would actually give in a design review, with a number in it.
Rep 9 — What eight heterogeneous cores are actually worth
Now correct Amdahl for the fact that your cores are not equal:
python3 amdahl.py --hetero 1,3,4 --p 0.95 --fractions 0.95 --cores 8
python3 amdahl.py --hetero 4,4,0 --p 0.95 --fractions 0.95 --cores 8
python3 amdahl.py --hetero 0,0,8 --p 0.95 --fractions 0.95 --cores 8
Three eight-core machines. Record the prime-equivalent core count and the corrected speedup for each.
Write down: the three pairs of numbers, and then reason about them rather than just reporting them. Which configuration has the highest corrected speedup, and which would you actually put in a phone? Those are not the same answer, and the gap between them is the whole chapter — say in two sentences what the highest-throughput configuration costs that the table does not show. (Area and idle power are the words you are looking for; §2.9’s relative_area column has the arithmetic.)
Reps 10–11: Placement, and the Number You Must Not Trust
Rep 10 — Build a three-row placement table
Here is the artifact Project 2 grades, in miniature. Three workloads, with synthetic teaching parameters — you did not measure these, and you must say so:
| Workload | Instructions | Deadline |
|---|---|---|
| A 120 Hz scroll frame | 24 M | 8.3 ms |
| A background photo-library index | 1.2 G | 60 s |
| A photo edit applied on user tap | 6.0 G | 1.0 s |
Run each one:
python3 placement_model.py --instructions 24e6 --deadline-ms 8.3
python3 placement_model.py --instructions 1.2e9 --deadline-ms 60000
python3 placement_model.py --instructions 6e9 --deadline-ms 1000
Now build the table with these columns: workload · chosen core class · why · what it costs (ms and mJ) · what I could not model. One row per workload. The third row will not have a comfortable answer — read what the tool says and deal with it honestly rather than pretending.
Write down: the three rows, and then two sentences. First, for the third workload: the single-core model says only one class meets the deadline — what would you actually do instead, and which tool in this chapter would you use to check it? Second, name one thing every row of this table is silent about. (There are several good answers; frequency, thermals, and “should this be on a CPU at all” are three of them.)
Rep 11 — Make an AI state a core count, then verify it
This rep exists so you watch the failure happen under controlled conditions rather than in a graded report.
Pick one real, currently-shipping mobile SoC. Ask a language model, in one message: “How many CPU cores does
Now verify it — every figure, against a primary source: the silicon vendor’s own product or specification page, or an architecture reference manual. Not a blog, not a spec-aggregator site, not a second AI.
Write down:
- The model’s claim, quoted.
- For each figure: verified / contradicted / not findable in any primary source. That third bucket is the interesting one, and it is usually the biggest.
- One sentence on which figure you would have been most likely to accept without checking, and what made it plausible.
Then the reflection that matters: the model’s answer was fluent, specific, and formatted like knowledge. Write two sentences on what that means for how you must read any number you did not measure or cite. AI usage: required — name the model and version, and state plainly that you used it as the subject of the experiment, not as a source.
Done? One Last Thing.
This is Project 2 in miniature — measure your own machine, then place a workload on it.
Run a concurrency sweep with the binary you already built. Each copy is an independent instance of the same fixed kernel, so as you add copies the OS has to place them on progressively less capable cores:
for t in 1 2 4 8; do
echo "=== $t concurrent copies ==="
for i in $(seq 1 $t); do ./migrate --kib 256 --rounds 200 > run-$t-$i.txt & done
wait
grep -h "warm (same core)" run-$t-*.txt
done
Then do all three steps:
- Measure. For each
t, record every copy’swarmmedian. Report the median-of-medians and the spread across copies, with your conditions stated per Appendix C. Predict first: at whattwill the copies stop agreeing with each other, and why? - Interpret. The spread across copies at high
tis the asymmetry of your CPU, measured. Compare it to the class inventory you took in Rep 1. Do the numbers agree with what the OS claimed? If not, what else could explain it — a shared cache, a frequency change, another process? - Place. Take one row of your Rep 10 table and write the paragraph you would hand a reviewer: the workload, the class you chose, the deadline it must meet, the energy it costs, the reason a cheaper class was rejected, and — required — one sentence naming what in your analysis is modeled rather than measured.
Keep this write-up. You have already drafted the core of placement-decision.docx.
Up next: Project 2 — Project 2: The Placement Study.