Repository navigation
D-DOMINO-MOV-0: bit flip, positional substitution and lazy FULL carry (probe) - #391
Conversation
… counters Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
The packed-u128 anchor was called once with a known-zero high half, so LLVM specialized it and the disassembly did not show the general function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
…eeping The plain flip ripple (F) is exact and beats native per-lane adds on dense and single-lane masks. Positional substitution and lazy FULL forwarding (P, L, PL) are exact but slower than F everywhere: their flag tests cost more than the XOR/AND they avoid. Two u64 planes and one u128 compile to the same code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23d6773b07
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
PL's ordinary ripple stored an all-ones result without setting FULL, unlike L, so an all-ones carry into it read and wrote the word instead of passing it as FULL -> EMPTY. The tests now check, after every add, that L and PL hold every logically all-ones column as FULL, plus a targeted complementary-mask case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
All tables re-measured on three tiers. PL's per-FULL-column cost is about 2.2 ns (was 1.85); verdicts are unchanged. Differences in rows whose code did not change are recorded as run and layout variance. Adds the new test and disable run Y7 to the report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
…y for U32x16 shifts half_add_mask(a, carry) = (a ^ carry, a & carry) and full_add_mask(a, b, c) = (a ^ b ^ c, majority) are the flip and carry_save3 laws proven in #391 and #389, now in simd_masking_ops and re-exported through ndarray::simd. Each u64 is one significance column of 64 independent counters; the carry belongs one column up in the same lane. Tests: both truth tables in every lane position, lanes 0/31/32/63 alone, 2000 random words (with zero, all-ones and alternating masks) against per-lane addition, and a ripple counter that keeps lanes apart while moving the carry to a neighbouring bit corrupts it. The wasm parity harness now also checks U32x16 << and >> with a distinct count per lane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMPh2Seh2WLccCksAdY
Summary
D-DOMINO-MOV-0 tests whether a bit-flip counter (a half adder per column, 64 lanes in a u64) can replace integer carry handling in the U9 abacus, and whether two refinements pay off: positional substitution (assign into a known-empty column) and a lazy FULL carry (a FULL column absorbs a carry as a state change).
Four candidates are implemented in plain Rust and measured on one AVX-512 VNNI Xeon, against native per-lane u64 sums and against D from #389 (merged): F (eager flip), P (substitution), L (lazy FULL), PL (both).
Verdicts
The bit flip crosses 64K exactly, and two u64 planes (R1) and one u128 (R2) cost the same. A two-plane abacus does not make carry escalation cheaper than native addition in general. The exception is dense or single-lane masks fed one at a time.
Review fix
Codex found that PL's ordinary ripple stored an all-ones result without marking the column FULL, unlike L. A later all-ones carry therefore read and wrote the word instead of passing it as FULL -> EMPTY, and the L-vs-PL comparison used two different FULL policies. PL now marks the column FULL the same way L does.
check_allasserts after every add that L, PL and PL-stale hold exactly the logically all-ones columns as FULL. A new test builds a column from complementary masks and checks the state-only carry. Restoring the old code turns both red (Y7). Every table below is a second full measurement taken after the fix.Key measurements
ns per 64 products, 64K boundary, L1 dense (products 65,024-65,025). Ranges are min - max of 3 runs of medians.
Counters alone, ns per add (B), and one add through n FULL columns (B'), VNNI:
Clone-only cost is about 5.1 - 5.5 ns (F), 4.9 - 5.0 (P), 5.5 - 5.7 (L), 5.7 (PL). Per FULL column (medians): F about 0.52 ns, P about 1.6 ns, L about 1.9 ns, PL about 2.2 ns (1.85 before the fix).
Normalizing P or PL after every block costs 74.7 - 74.8 and 107 - 110 ns per 64 products on VNNI, so only the deferred form is usable.
Some rows moved between the two measurements even though their code did not change. F on random masks went from 6.4 to 8.1 ns on VNNI, and the streamed rows rose for every counter alike. The report attributes these to run and code-layout variance, not the fix. One conclusion follows from it: F is now about 16% slower than native on random masks on every tier, where the first run had it equal on VNNI.
Assembly findings
flipis 5 scalar instructions, no ternlog.pair_addand R2packed_addcompile to the same 10 instructions on native and v3. LLVM sees through the u128 packing.Coverage findings
The #388/#389 tests (15 in
tests/u9_abacus.rs, 5 intests/u9_head_to_head.rs) were audited from source. Gaps found:Correctness
tests/u9_domino.rsadds 8 tests, passing on native, v4, v3, v2 (qemu) and aarch64 (qemu); clippy and the shuffle guard are clean. The tests are: half-adder per lane, random adds against a per-lane u128 oracle in both orders for six variants (F, P, L, PL, P-stale, PL-stale) with the FULL invariant checked after every add, FULL chains of 1 to 64 columns, the 64K boundary (bit 16 flips on crossing XOR preset, bit 17 on crossing AND preset), product sums against native and D, overflow refusal, an XOR-made FULL column carrying by state, and R1/R2 agreement.Seven disable runs each turn tests red:
Not in this PR
Run:
cargo run --release --example u9_domino --features std cargo test --release --test u9_domino🤖 Generated with Claude Code
https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v