D04 Exercises - The Wire¶
Four drills, one per thing the module claims you should be able to do: read a topology capture and turn it into a decision, price the host-staged path against the direct one, find the injection ceiling a node was built with, and measure the fabric well enough to catch it lying to you. They are the "Coding & Algorithms" drills from section 9 of the module, turned into starter files with TODOs.
The three Python drills run as they ship. Each prints its tables with
not implemented in every cell until you fill the TODOs in, so you always have a working
harness and never a blank page. Standard library only - no numpy, no torch, nothing to
install.
| Drill | File | What it tests |
|---|---|---|
| 1 | 01_topology_classifier.py |
Normalize an NVIDIA and an AMD topology capture into one enumeration, then refuse a placement whose worst pair is not a direct link |
| 2 | 02_staging_cost.py |
Serial and pipelined bounds for the staged and direct paths, and the PCIe-to-wire ratio where they diverge |
| 3 | 03_injection_ceiling.py |
Per-GPU network share, the ratio against the scale-up tier, and the adapter count a target ratio costs |
| 4 | 04_peer_copy_harness.md |
Extend lab 2 into a sweep over every ordered pair and several sizes, and flag any pair whose bandwidth contradicts its class |
Drill 4 is a specification rather than a starter file, because the thing being exercised is a design decision - what "inconsistent with its class" means on a machine whose absolute numbers you are not allowed to assume - and not an algorithm. It needs at least two GPUs to run.
How to work these¶
- Do the arithmetic by hand first. Everything drills 2 and 3 need is derived in section 2.3 and section 3.6. Reaching for the tool before you have a number practises the wrong skill.
- Then check against the tools.
../tools/implements the same closed forms and the same parser, so a disagreement means one of the two has a bug. Find out which before moving on. - Answer the questions at the bottom of each file. They are the part an interview actually asks. The code gets you the number; the question is what the number licenses you to say.
# From the module directory (one level up):
make run-exercises # runs all three Python harnesses
python exercises/01_topology_classifier.py
python tools/topology_report.py --help
# Or from here:
python 02_staging_cost.py
Drill 1 reads the format samples in ../samples/.
[!IMPORTANT] Those samples are synthetic examples of the output format, hand-written to exercise the parser. They are not a capture from any machine, they contain no hostname, user or serial number, and the topology they describe is invented. Every number they lead to is a property of the file, not of hardware. Run the same code on your own capture - lab 1 shows how to take one.
Acceptance criteria¶
A drill is done when its harness prints these and you can defend the answers at the bottom of the file. Every figure below is what the reference solution prints, so a mismatch is a bug in one of the two.
Drill 1 - the classifier.
- On the NVIDIA sample,
GPU0-GPU1andGPU2-GPU3come outdirect-link, every pair that crosses the two islands comes outcross-socket, and each GPU reaches its own adapter throughpcie-switchand the far one throughcross-socket. - The ROCm sample describes the same node and must produce the same classes, which is the
whole point of normalizing two vendors onto one enumeration.
PCIEbetween GPUs in different NUMA nodes iscross-socket; within one node it ispcie-host, because rocm-smi cannot tell a switch-local path from a host-bridge one and guessing the better of the two would be inventing a fact. - The third sample carries the code
QQ9, which is not a real nvidia-smi code. It must come outunknownand be listed as an unclassified code. A classifier that maps it to anything else has failed the only test in this drill that matters. worst_class_in_groupreturnsdirect-linkforGPU0,GPU1,cross-socketfor all four, and raisesValueErrorfor a group with fewer than two GPUs.- A pair missing from the capture is
unknown, not skipped. Confirm with the truncated case the harness prints last. - Cross-check the whole thing with
python ../tools/topology_report.py --input ../samples/nvidia_topo_synthetic.txt --group 0,1,2,3.
Drill 2 - staged against direct.
- The reference case reproduces section 2.3 exactly: 1e9 bytes at PCIe 20 GB/s and wire 10 GB/s gives 0.30 s staged and 0.20 s direct on the serial bound. If you get 0.322 and 0.215 you used a gibibyte; that 7% is the smallest version of the unit mistake section 5.2 warns about.
- The pipelined bound gives a ratio of 1.00x when PCIe is 2x or 4x the wire, and 2.00x when PCIe is equal to or narrower than the wire. Both bounds are correct and they disagree; question (a) is about which one your workload lives on.
crossover_pcie_bpsreturns exactly twice the network bandwidth: 10, 20 and 50 GB/s for wires of 5, 10 and 25 GB/s.- The pipelined ratio is identical at 64 KiB, 1 MiB and 1 GB. The crossover does not move with message size, and question (b) asks you to show why from the algebra.
- All three rejection cases raise
ValueErrorwith a one-line message naming the offending value.
Drill 3 - the injection ceiling.
- With the shipped illustrative figures, eight GPUs behind one adapter get 3.125 GB/s each, a ratio of 0.0078 against the scale-up tier - about 1:128.
- Doubling adapters doubles the share exactly; doubling GPUs halves it exactly. At
n_nic == n_gputhe ratio is 0.0625 at 2, 8 and 64 GPUs alike, which is the property that makes parity the design goal. - Reaching a 0.25 ratio on an eight-GPU node needs 32 adapters with these figures. The answer rounds up: 3.2 adapters is not a thing you can buy, and rounding down returns a count that misses the target.
- Zero adapters raises
ValueError, neverZeroDivisionErrorand never0.0. A node with no adapter is a configuration to fix, not a share of nothing. - Cross-check with
python ../tools/injection_model.py --gpus-per-node 8 --nics-per-node 1 --nic-gbs 25 --scaleup-gbs 400.
Drill 4 - the harness.
- Builds clean, checks every return code, verifies byte-for-byte at every pair and every size, and frees everything it allocates.
- Sweeps ordered pairs, both directions. A harness that measures only
i < jreports the better direction and hides the worse one. - States its consistency rule in a comment in two sentences, in your own words, with its blind spot named. The reference's blind spot is a uniformly slow machine, where no pair contradicts any other and the check says nothing at all.
- Exits with a one-line message on a single-GPU machine, not a crash and not a table of zeroes.
Shared assumptions unless a drill says otherwise¶
| Quantity | Value | Why this one |
|---|---|---|
| Link classes | self, direct-link, pcie-switch, pcie-host, cross-socket, unknown |
one enumeration for both vendors, per module section 5.1 |
| Unknown handling | reported, never guessed, and ranked worst | a class you cannot name is one you cannot certify |
| Bandwidth unit | GB/s with 1 GB = 1e9 bytes | the convention link vendors quote; see bandwidth |
| Staged path | 4 PCIe traversals, 4m of host traffic, 1 wire traversal | module section 2.3 |
| Direct path | 2 PCIe traversals, no host traffic | RDMA and GPUDirect |
| Adapter, scale-up figures | 25 GB/s and 400 GB/s per GPU | illustrative orders of magnitude, not a datasheet quote - only the ratio is load-bearing |
Drills 2 and 3 compute an analytic model, never a measurement, and both harnesses say so in their banner. Drill 1 reports what a file says and names the file. Drill 4 is the only one that measures anything, and what it measures is your machine.
Reference answers with commentary: ../solutions/. Read them after you
have written yours down - they contain the reasoning you are being assessed on, not just
the code.
Related: module README | glossary | references