perf(evolution): ⚡ carry cross-rank queries as positions, not dense words - #235
perf(evolution): ⚡ carry cross-rank queries as positions, not dense words#235diagonal-hamiltonian wants to merge 1 commit into
Conversation
…ords
A cross-rank query carried the partner's whole dense monomial -- W words
plus a phase word, at a fixed stride. At 250 modes that is 72 B a
record; at 512 modes, 136 B. The payload is ~93% zero: the terms this
engine cares about have small popcount, so nearly every word is empty.
The record now carries the ascending set-bit POSITIONS instead: six
inline u16 positions, a 16-bit k, an i8 phase and a flags byte, packed
into two words -- 16 B, with inline continuation records of eight
positions each for the rare wider term.
WHY IT WINS. The two alltoallv legs are ~68% of layer time, and that
funnel is bytes-through-one-serialised-thread, not latency-bound, so the
record's width is close to the whole cost.
| shape | B/record | exchange_s | build_graph | agree |
|------------|---------------|-----------:|------------:|------:|
| (512, 6) | 136.00 -> 16.00 | 0.4960x | 1.56x faster | 6/6 |
| (250, 10) | 72.00 -> 29.93 | 0.6976x | 1.26x faster | 6/6 |
| (128, 6) | 40.00 -> 16.00 | 0.7809x | 1.15x faster | 6/6 |
| (32, 6) | 16.00 -> 16.00 | 1.0003x | flat | -- |
| (250, 4) | 72.00 -> 16.00 | 0.9774x | flat | -- |
THE PREDICTOR IS GiB REMOVED FROM THE WIRE, NOT THE SHRINK RATIO, and
the two flat cells are why -- they are flat for opposite reasons.
(250,4) has the second-largest shrink in the sweep (4.5x) and is dead
flat, because its entire cross-rank query volume is 5,940 records: at
cutoff 4 nearly every product exceeds the cutoff and dies before it ever
needs resolving. (32,6) is flat because at kWords == 1 the old record
was already 16 B, so the two formats coincide -- provably inert, not
merely un-regressed. Sorted by GiB removed, exchange_s is monotone
across all five shapes; sorted by shrink ratio it is not.
ACROSS WORLD SIZES, propagate is 1.63x / 1.97x / 1.52x at 1 / 2 / 4
nodes, resolved 6/6 at each. Note the per-byte advantage DECAYS with
world size (exchange_s 0.4960x -> 0.5488x) while the end-to-end win does
not, because exchange's share of layer time climbs 67% -> 85% -> 89%.
Do not quote the wire ratio as if it predicted the wall: as N grows the
two move in opposite directions.
CORRECTNESS, before any timing. 12/12 bit-identical operator
fingerprints over {graph, propagate, pauli} x (R,S) in {1x1, 2x1, 2x8,
4x8} -- every term, in enumeration order, coefficients hashed as raw
IEEE-754 bits, per rank and not merged. Bit-identical rather than
tolerance-equal is the right bar and agreement at 1e-7 would have been a
FAILURE: the format changes no routing decision and no record order, and
record order IS the floating-point accumulation order, since Resolve.h
mints each miss's term index in (sender, record) order. Agreement only
to a tolerance would mean something reordered.
The receive side keeps the query in the positions it arrived as instead
of rebuilding a dense monomial: incoming_s 0.79x, 8/8 over two cells,
p=0.0078 -- about twice what its instruction count predicted, because it
deletes a ~110 KB-per-call staging array and so stops evicting the hash
table the probe is about to walk. The insert path group-prefetches its
slot addresses the way find_batch already did: incoming_s 0.90x and
insert_s 0.88x, 8/8 each. Both are memory-system wins, which is the
pattern here -- the wins on this work have been memory-system wins and
the losses instruction-count guesses.
DELETES cpp/tests/fused_query_codec_tests.cpp. It tested the dense
fused-query helpers, which no longer exist. The dense codec itself was
already written as an independently-authored oracle, so it MOVED to
cpp/tests/dense_query_reference.h rather than being deleted: with one
production format, a round-trip test can only ask whether the codec
agrees with itself, and that catches a decoder mirroring its encoder's
mistake not at all. The reference shares no code with CompactQuery --
different layout, different width, different offset arithmetic -- and is
frozen, not a second implementation to keep in sync.
Assisted-by: ClaudeCode:claude-opus-5
|
Docs preview: https://pr-235.monoprop-docs.pages.dev |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## pr/digest-cutoff #235 +/- ##
=================================================
Coverage 97.70% 97.70%
=================================================
Files 14 14
Lines 742 742
Branches 98 98
=================================================
Hits 725 725
Misses 12 12
Partials 5 5
Flags with carried forward coverage won't be shown. Click here to find out more. |
|
|
🤖 AI text below 🤖 Holding this as draft: the encoding is under revision. Nothing here is wrong — this is a scope Measured this week on the two shipped lattice models, the compact record's win turns out to be a
The premise that the cutoff bounds So the deletion of the dense path here is defensible — the compact record is never materially worse Two findings from that follow-up work bear directly on this file:
Reopening once the encoding settles, so the CI state here is worth keeping rather than closing. |



🤖 AI text below 🤖
Summary
A cross-rank query carried the partner's whole dense monomial —
Wwords plus a phase word, at a fixed stride. At 250 modes that is 72 B a record; at 512 modes, 136 B. The payload is ~93% zero: the terms this engine cares about have small popcount, so nearly every word is empty.The record now carries the ascending set-bit positions instead: six inline
u16positions, a 16-bitk, ani8phase and a flags byte, packed into two words — 16 B, with inline continuation records of eight positions each for the rare wider term.Why it wins. The two
alltoallvlegs are ~68% of layer time, and that funnel is bytes-through-one-serialised-thread, not latency-bound, so the record's width is close to the whole cost.exchange_sbuild_graphThe predictor is GiB removed from the wire, not the shrink ratio, and the two flat cells are why — they are flat for opposite reasons.
(250,4)has the second-largest shrink in the sweep (4.5×) and is dead flat, because its entire cross-rank query volume is 5,940 records: at cutoff 4 nearly every product exceeds the cutoff and dies before it ever needs resolving.(32,6)is flat because atkWords == 1the old record was already 16 B, so the two formats coincide — provably inert, not merely un-regressed. Sorted by GiB removed,exchange_sis monotone across all five shapes; sorted by shrink ratio it is not.Across world sizes,
propagateis 1.63× / 1.97× / 1.50× at 1 / 2 / 4 nodes, resolved 6/6, 6/6 and 12/12. Note the per-byte advantage decays with world size (exchange_s0.4960× → 0.5488×) while the end-to-end win does not, because exchange's share of layer time climbs 67% → 85% → 89%. Do not quote the wire ratio as if it predicted the wall: as N grows the two move in opposite directions.Correctness, before any timing
12/12 bit-identical operator fingerprints over
{graph, propagate, pauli} × (R,S) ∈ {1×1, 2×1, 2×8, 4×8}— every term, in enumeration order, coefficients hashed as raw IEEE-754 bits, per rank and not merged (a merged digest would not notice terms moving between ranks).Bit-identical rather than tolerance-equal is the right bar, and agreement at 1e-7 would have been a failure: the format changes no routing decision and no record order, and record order is the floating-point accumulation order, since
Resolve.hmints each miss's term index in(sender, record)order. Agreement only to a tolerance would mean something reordered.The receive and insert sides
Keeping the query in the positions it arrived as, instead of rebuilding a dense monomial, measures
incoming_s0.79× (8/8 over two cells, p=0.0078) — about twice what its instruction count predicted, because it deletes a ~110 KB-per-call staging array and so stops evicting the hash table the probe is about to walk. Group-prefetching the insert path the wayfind_batchalready did measuresincoming_s0.90× andinsert_s0.88× (8/8 each).Both are memory-system wins, which is the pattern in this work: the wins have been memory-system wins and the losses instruction-count guesses.
Deletes a test file on
maincpp/tests/fused_query_codec_tests.cpp— it tested the dense fused-query helpers, which no longer exist.The dense codec itself was already written as an independently-authored oracle, so it moved to
cpp/tests/dense_query_reference.hrather than being deleted. With one production format, a round-trip test can only ask whether the codec agrees with itself, and that catches a decoder mirroring its encoder's mistake not at all. The reference shares no code withCompactQuery— different layout, different width, different offset arithmetic — and it is frozen: not a second implementation to keep in sync.Verification
ctest -L serial— 244/244 green.pytest tests --with-mpigreen at 1, 2 and 4 ranks.Checklist
docs/,CONTRIBUTING.md) if neededCHANGELOG/ release notes updated if applicable — release notes are generated from the PR titleAI/LLM disclosure