notes 072

Instrument · kernel autopsy

The ALU ledger: one doc through the 072 kernel

After the unsafe width cast, LLVM re-vectorized the tile loop: one iteration now compares one doc against 8 queries in SIMD lanes. This page maps the emitted assembly (objdump, Zen5 EPYC 9R45) onto the machine's execution resources — so the next optimization can be read straight off the ledger.

1106
QPS @ 10M, C=2000, recall 0.974 (072, batch=8)
~79
uops per doc-iteration (8 comparisons)
~73%
of that budget re-establishes loop-invariant facts
8/cyc
dispatch width — the resource that binds

The rule this page keeps applying: throughput = uops per item ÷ dispatch width, and the uops that count are the ones your answer format forces. The math below is ~18 uops per doc. Everything else is the machine re-proving, ten million times, facts that were fixed before the loop began.

fig. 1the dispatch ledger — one doc's uop budget
79 uops/doc · 9.9 cyc/doc · 1.23 cyc/comparison
re-established every doc (invariant — waste)hamming mathstore + loop

Hover a segment. Width = share of the doc-loop's dispatch budget (8 uops/cycle, the binding resource). One iteration = one doc × 8 register-resident queries.

The red segments are the finding. Block 1 rebuilds the 8 query addresses from Rust fat pointers — lane surgery that exists because qrows is a Vec<&[u64]>. The gathers then refetch the same 256 bytes of query data, every doc. Neither computes anything about Hamming distances. LLVM cannot hoist either one: the accumulator store writes through a heap Vec, and the compiler can't prove that store doesn't overwrite the query pointers. The doc side shows the counterfactual — its four words sit pre-broadcast in zmm1–4, hoisted, free.

Toggling to 073 — planar shows the same iteration with queries transposed once per tile into a stack array [[u64; 8]; 4]: addresses become compile-time offsets, gathers become four hoistable loads, the aliasing question evaporates (stack can't alias heap), and 58 of 79 uops come off the ledger.

fig. 2port pressure — what binds, before and after
dispatch (8 uops/cyc)← binds100%

THE binding resource. 79 uops/doc ÷ 8-wide ≈ 10 cycles/doc. Every bookkeeping uop displaces math here.

load/AGU (2×512b + AGUs)85%

Gathers hammer the AGUs with 32 scattered qword fetches per doc. Planar: one 32 B doc line per doc.

vector ALU (4×512b)38%

XOR/add — capacity to spare in both. The shuffle-class subset (2 pipes) eats Block 1's permutes in 072.

popcount (2 of the 4)20%

4 zmm popcounts per doc. The theoretical floor: 2/cyc = 4 docs/cyc. 073 moves it toward binding — which is the goal.

scalar ALU (6)10%

Loop control, heap compares. Six wide — never the limiter; it steers while vector pipes chew.

Dispatch is pinned at 100% in 072 — the front-end cannot issue the bookkeeping fast enough to keep the vector pipes busy. The popcount pipes (the theoretical ceiling: 2×512-bit per cycle = 4 docs/cycle) idle at ~20%. 073 flips the profile: dispatch relaxes, popcount rises toward binding — which is the goal. A kernel is done when its dedicated instruction is the bottleneck.

fig. 3the emitted hot loop, block by block (objdump)
vmovq 0x70(%rax),%xmm7load fat-ptr pieces from qrows (Vec<&[u64]>: ptr,len,ptr,len…)
vpermq $0xe8,(%rax),%ymm6pluck the even qwords — addresses; discard the lens
vpunpcklqdq %xmm7,%xmm8,%xmm7interleave two more pointers
vinserti64x4 $1,%ymm7,%zmm5,%zmm5stitch → zmm5 = 8 query ADDRESSES

Worth savoring in Block 2b: there is no horizontal reduction anywhere. Every previous kernel paid 3–5 shuffle-class uops per doc converting lane counts into the one scalar the heap demands; the vectorized loop keeps lane j as query j's running distance and stores all eight at once. The compiler independently arrived at the answer-format fix we designed by hand — once the unsafe cast gave it a provable fixed trip count.

Numbers: uop counts and port mappings are estimates from the objdump walk-through and the Zen5 port model (notes 071/072); QPS is measured (CV ≤ 0.9%). The 073 column is a prediction — the experiment exists to falsify it.