Appendix L

Hardware Measurements (DGX Spark GB10)

The first measured evidence in this package: achieved streaming bandwidth, a two-term decode model fitted on two frameworks, the raw tables behind both fits, the method, and the two measurement bugs found and fixed on the way.

This appendix is the package's only source of Measured evidence.

Everything below was run on named hardware on 19 August 2026 and is reported with its method, its repetition count and its raw data. Nothing here is simulated, modelled or projected. Every other class of evidence in this package — Analytical model, Simulated (provisional), Vendor specification, External literature, Design target, Illustrative — is defined and located in Appendix K §K.1. The simulation results, which are a different and weaker class of evidence, are in Appendix N and always carry a provisional caveat.

L.1 Platform and Model Geometry

ParameterValueEvidence
MachineDGX Spark, GB10 Grace BlackwellMeasured
Memory128 GiB coherent unified LPDDR5XVendor specification
Memory bandwidth, spec figure273 GB/sVendor specification
Date of runs19 August 2026Measured
Model under testQwen2.5-7B-Instruct, fp16Measured
Layers28Measured
KV heads4Measured
head_dim128Measured
KV per token56 KiB/tokenAnalytical model
Weights resident15.2 GB = 14.16 GiBMeasured
Scripts../scripts/bw_wall.py, ../scripts/vllm_slope.py

The KV figure follows the standard sizing identity of Appendix C: 2 × 28 layers × 4 KV heads × 128 × 2 B = 57,344 B = 56 KiB/token. The decisive term is KV heads, not query heads — the same correction that governs the Llama capacity table in CANONICAL-NUMBERS.md §2.

L.2 Achieved Streaming Bandwidth

A streaming copy kernel (read + write), 4 GiB buffers, median of 20 repetitions. Measured, script ../scripts/bw_wall.py.

236.5 GB/s  =  87% of the 273 GB/s spec figure
Achieved streaming bandwidth, GB10 unified LPDDR5X, median of 20 at 4 GiB buffers
What this retires

Earlier drafts applied an undefended ~50% "effective bandwidth" haircut to the theoretical figure. That haircut is retired by this measurement, and it was wrong in the pessimistic direction: real achieved bandwidth on this part is 87% of spec, not 50%. The consequence is not local. The retracted v4.0 TTFT claim of 15.6× rested on a 16.4 GB/s sustained-offload assumption of exactly this kind, and its retraction (see CANONICAL-NUMBERS.md §7, and the reconciliation note in Appendix K) follows directly from the fact that a measured achieved-versus-spec ratio contradicts it.

L.3 The Decode Model Is Two-Term, Not One-Term

The single-bandwidth roofline t = (W + KV) / B does not hold on this hardware. Sweeping resident KV volume across a batch × context grid and fitting a two-term model t = a + b·KV_GiB separates the constant weight read from the KV read that grows with context:

FrameworkFit (ms)nWeight bandwidthKV bandwidth
HuggingFace + SDPA, unpaged100.3 + 16.34·KV_GiB70.991151.5 GB/s65.7 GB/s
vLLM 0.20.1 + FlashAttention-2, paged73.9 + 17.58·KV_GiB90.945205.7 GB/s61.1 GB/s

Evidence: Measured (the times and the fit); Analytical model (the two bandwidths, which are derived from the fit coefficients and the resident weight bytes, not read off an instrument).

The derived effective bandwidths follow from the two coefficients. The intercept a is the KV-independent term and is charged against the 15.2 GB of resident weights; the slope b is milliseconds per GiB of resident KV:

Weight
15.2 GB ÷ 100.3 ms = 151.5 GB/s (HF)  ·  15.2 GB ÷ 73.9 ms = 205.7 GB/s (vLLM)
The intercept is the fitted context-independent term. It includes weight traversal and other fixed decode work; it does not depend on resident KV volume in this model.
KV
1 GiB ÷ 16.34 ms = 65.7 GB/s (HF)  ·  1 GiB ÷ 17.58 ms = 61.1 GB/s (vLLM)
The slope is the per-step KV traversal. It is the term that grows with batch × context.

L.3.1 As a fraction of the bandwidth actually achieved

The reference point is not the 273 GB/s spec figure but the 236.5 GB/s the same machine achieves on a streaming copy (§L.2). Against that:

ReadHuggingFace + SDPAvLLM + FlashAttention-2
Weight read151.5 GB/s = 64%205.7 GB/s = 87%
KV read65.7 GB/s = 28%61.1 GB/s = 26%

vLLM extracts near-peak achievable bandwidth on weights and roughly a quarter of it on KV, in the same kernel launch, on the same memory.

L.4 The Three Results That Carry the Argument

1. KV is read at roughly a quarter of achievable memory bandwidth

26–28% of 236.5 GB/s across both frameworks. Measured. This is an HBM-side property of the attention read pattern, not a property of what sits behind HBM.

2. The two-term fit implies a ~3.4× effective-path ratio on this hardware

205.7 ÷ 61.1 = 3.4×, taken within the vLLM configuration so that the comparison is like-for-like. Measured inputs, ratio Analytical model. The intercept contains fixed decode work in addition to weight traffic, so this is not a direct measurement of the cost of an individual weight byte versus an individual KV byte. The consequence for the objective function of Appendix M §M.4 is direct: its byte-counting terms must be weighted, because a KV byte and a weight byte are not interchangeable.

3. Kernel substitution alone did not eliminate the measured KV slope

Two independent frameworks, two different attention implementations, paged versus unpaged KV layout — and the KV slopes land within 8% of each other (16.34 vs 17.58 ms/GiB). Over the same change the weight read improves by 34% (151.5 → 205.7 GB/s), reaching 87% of achievable bandwidth. Within these two tested stacks, switching attention implementation and KV layout did not materially change the fitted KV slope. This is evidence about this GB10/Qwen2.5-7B operating region, not a claim about every kernel, GPU architecture, page size, model, or decode regime. Measured.

L.5 What This Settles

L.5.1 Reducing KV traffic is the first-order opportunity exposed by this measurement

The measurement supports a narrower conclusion: reducing KV traffic is a first-order opportunity. Quantisation or compression reduces bytes per KV element; sparse or hierarchical selection can reduce the number of KV elements consumed. Placement remains necessary for capacity, reuse and movement, but moving a resident working set to another tier does not by itself reduce the bytes that dense attention must consume once those bytes are presented to the GPU. The relative end-to-end benefit of compression, selection and placement remains an experimental question (CANONICAL-NUMBERS.md §5).

L.5.2 Capacity was never the binding constraint here

Every configuration measured fit comfortably in 128 GiB. Throughput collapsed anyway:

150 tok/s at batch 16 × 4K  →  46 tok/s at batch 16 × 16K
With memory to spare and nothing evicted — the fall is bandwidth, not capacity

Nothing was evicted, nothing was offloaded, and no placement policy ran. A 3.3× fall in decode throughput arrived purely from re-reading more KV per step. Any argument that reaches for a capacity mechanism to explain a throughput fall of this shape is reaching for the wrong mechanism.

Read this against the simulation, not instead of it

§L.5.2 says capacity was not binding in the configurations measured here — one 7B model on one 128 GiB machine. It is not a claim that capacity never binds; the 421 GiB footprint studied in Appendix N is a regime where it plainly does. The two appendices answer different questions and must not be merged into a single narrative. Appendix N's numbers are Simulated (provisional); nothing on this page is.

L.6 Raw Data

Both tables in full. Each row is the median of 20–30 timed decode steps after an untimed warm run, with the step time isolated by the differencing procedure of §L.7.

L.6.1 HuggingFace + SDPA (fp16, chunked prefill, unpaged)

batchctxKV GiBmeasured ms/steptok/sKV % of bytes
140960.22104.69.62%
1163840.88113.98.86%
1327681.75128.57.811%
440960.88111.835.86%
4163843.50155.425.720%
4327687.00212.418.833%
1640963.50165.196.920%

n = 7. Fit t = 100.3 + 16.34·KV_GiB ms, R² = 0.991. Script ../scripts/vllm_slope.py carries the HuggingFace path as well as the vLLM path.

L.6.2 vLLM 0.20.1 + FlashAttention-2 (fp16, paged, prefix caching disabled)

batchctxKV GiBmeasured ms/steptok/s
140960.2295.110.5
1163840.88101.59.8
1320001.71107.89.3
440960.8899.940.0
4163843.50117.434.1
4320006.84162.224.7
1640963.50127.5125.5
161638414.00345.946.2
3240967.00185.0173.0

n = 9. Fit t = 73.9 + 17.58·KV_GiB ms, R² = 0.945. The 16 × 16384 row sits 26 ms above the fit and is the single noisiest point in either table; see §L.9. Script ../scripts/vllm_slope.py.

One unreconciled figure, stated rather than smoothed over

The 150 tok/s → 46 tok/s collapse quoted in §L.5.2, in data/measured_results.md §3 and in CANONICAL-NUMBERS.md §1.3 has its low end exactly in this table: 46.2 tok/s at batch 16 × 16384. Its high end does not. The batch-16 × 4K row here reads 125.5 tok/s (16 ÷ 127.5 ms), not 150. The two are not reconciled in the source material, so the discrepancy is reported rather than quietly resolved. CANONICAL-NUMBERS.md governs the quoted headline; the table above governs any re-derivation, and a re-derivation from the table gives a 2.7× fall rather than 3.3×. The direction and the conclusion — capacity was not binding, bandwidth was — are unaffected either way. A repeat of the batch-16 sweep should settle it, alongside the 16 × 16384 outlier noted above.

L.7 Method

Decode step time is isolated by differencing, which cancels prefill and warm-up without requiring either to be modelled:

tstep = ( t(N) − t(2) ) ÷ ( N − 2 )   with N = 32
Two generations per configuration; the difference removes every fixed cost they share
ControlSettingWhy
Warm runOne untimed generation before every timed configurationRemoves allocator, autotune and JIT effects from the timed window
RepetitionsMedians of 20–30 stepsMedian rather than mean, so a single scheduling outlier does not move the fit
Batch constructionDistinct token ids per sequenceGuarantees no KV is shared between sequences in a batch — see bug 2 in §L.8
Prefix cachingenable_prefix_caching=False (vLLM)Same reason, enforced at the framework level as well as at the input
Batch shapeFixed-length uniform batchesMakes resident KV exactly computable as B × L × 56 KiB

L.8 Provenance Discipline — Two Bugs Found and Fixed

Both are recorded, and recorded in full, because both produced plausible-looking wrong answers. A measurement appendix that lists only the runs that worked is not a measurement appendix.

L.8.1 Bug 1 — silent fp32 load

torch_dtype= is deprecated in current transformers and was silently ignored. Weights loaded in fp32 — 28.4 GiB instead of 14.16 GiB — and the run triggered the OOM killer. The failure mode is worse than it sounds: unified memory surfaces a CUDA OOM as a Linux OOM kill, which destroys the process without a CUDA error and obscures the cause entirely.

Fix. Use dtype=. The script now prints resident weight bytes and dtype at load, so a recurrence is visible in the log rather than inferred from a corpse.

L.8.2 Bug 2 — prefix-sharing artifact

The first vLLM run generated identical token ids for every sequence in the batch. vLLM's prefix caching therefore stored one shared copy of the KV, so true resident KV was L × bpt, not B × L × bpt. The x-axis of the fit was inflated by a factor of B, and the apparent KV bandwidth came out at:

342.9 GB/s  >  273 GB/s bus
Impossible — and it is the impossibility that exposed the bug

Fix. Distinct tokens per sequence, plus enable_prefix_caching=False.

The lesson, which is the reason this section exists

Any measured bandwidth above the bus is a bug, always. That rule is what caught this one. The instructive part is that bug 2 did not produce an obviously broken number. It produced a better number — one that would have supported a more exciting conclusion ("vLLM is 5× more efficient on KV") and that no amount of code inspection was going to volunteer. It was caught by checking the result against a physical bound, not by reading the script. A measurement that flatters the hypothesis deserves more scrutiny than one that does not, and the only reliable scrutiny is a bound the result cannot legally cross.

L.9 Limitations

L.10 The Next Measurement — fp8 KV

kv_cache_dtype=fp8 halves KV bytes per token, from 56 KiB to 28 KiB. That run is the direct test of the compression claim of §L.5.1, and it has exactly two possible outcomes. Both are named here in advance, so that neither can be presented afterwards as the one that was expected.

OutcomeSignatureWhat it would mean
A — bytes are what matter Slope holds near 17.6 ms/GiB while the GiB halve ~2× on the KV term. Confirms that the wall is a byte count and that byte reduction materially improves the KV term on this configuration. The objective function's weighting in Appendix M §M.4 stands as written.
B — dequantisation cancels the saving Slope roughly doubles to ~35 ms/GiB Dequantisation cost cancels the byte saving. fp8 buys capacity but not bandwidth on this hardware, and any claim that compression should be preferred over placement would remain unsupported; representation overhead must be measured separately.

Either outcome is a result. The second would be the more interesting one, and it is the one that would cost this package a conclusion it currently holds — which is precisely why it is stated here before the run rather than after it.