Skip to content

perf/virtual xor split - #558

Open
gabriel-barrett wants to merge 7 commits into
mainfrom
perf/virtual-rotr-chain-output
Open

perf/virtual xor split#558
gabriel-barrett wants to merge 7 commits into
mainfrom
perf/virtual-rotr-chain-output

Conversation

@gabriel-barrett

@gabriel-barrett gabriel-barrett commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

Fuse Blake3’s bytewise XOR and non-byte-aligned rotations using new u8_xor_split7 and u8_xor_split4 operations.

The old rotation-chain operations are no longer needed and have been removed completely.

Details

For two bytes a and b, let:

x = a XOR b

The new operations return the pieces required to rotate x:

u8_xor_split7(a, b) = (
                       x >> 7,
                       (x & 0x7f) << 1
                       )

u8_xor_split4(a, b) = (
                       x >> 4,
                       (x & 0x0f) << 4
                       )

Each operation is backed by one existing-style two-byte lookup. Four calls provide the pieces needed to construct a complete rotated u32 using only field additions and static reordering.

For example, rotation by seven bits is assembled as:

(h0, l0) = u8_xor_split7(a0, b0)
(h1, l1) = u8_xor_split7(a1, b1)
(h2, l2) = u8_xor_split7(a2, b2)
(h3, l3) = u8_xor_split7(a3, b3)

result = [
          h0 + l1,
          h1 + l2,
          h2 + l3,
          h3 + l0,
          ]

The additions are safe byte values because the high and shifted-low pieces occupy disjoint bit ranges.

Rotation by twelve bits uses the same construction with four-bit splits and a static byte permutation.

Blake3 changes

The following patterns:

u32_rotr7(u32_xor(a, b))
u32_rotr12(u32_xor(a, b))

are replaced with:

u32_xor_rotr7(a, b)
u32_xor_rotr12(a, b)

For each rotated word, this changes the cost from:

4 XOR lookups + 2 rotation-chain lookups = 6 lookups

to:

4 XOR-split lookups

The auxiliary-column cost remains eight columns per word, but execution performs less intermediate work and two lookups are eliminated.

Removed operations

The fused paths make these operations and their word-level wrappers unused:

u8_chain_rotr7
u8_chain_rotr4
u32_rotr7
u32_rotr12

They have been removed from:

  • Aiur source, typed, simple, concrete, and bytecode IRs
  • Type checking, lowering, layout, and code generation
  • Source and bytecode evaluators
  • Rust execution, constraints, trace generation, and FFI decoding
  • The Bytes2 lookup table

As a result, the Bytes2 table shrinks from:

Multiplicity columns: 12 → 10
Preprocessed columns: 20 → 14

Additional cleanup

The redundant packed equality assertions in u32_add and u32_add3 were removed.

Their carries are already virtual expressions:

(a + b - result) / 2^32
(a + b + c - result) / 2^32

Constraining those expressions to their valid carry domains, together with range-checking the result bytes, uniquely pins the wrapping sums.

Testing

  • Added prove/verify coverage for both XOR-split operations.
  • All seven Aiur library tests pass.
  • Cargo formatting and focused compilation checks pass.
  • Generated code must be refreshed separately.

@gabriel-barrett

This comment was marked as outdated.

@argument-ci-bot

This comment was marked as outdated.

@gabriel-barrett
gabriel-barrett force-pushed the perf/virtual-rotr-chain-output branch from 82ef2a8 to d3b5b55 Compare August 13, 2026 17:21
@gabriel-barrett
gabriel-barrett force-pushed the perf/virtual-rotr-chain-output branch from d2d69d7 to 7b72efc Compare August 13, 2026 17:23
@gabriel-barrett

This comment was marked as outdated.

@argument-ci-bot

This comment was marked as outdated.

Plus the trailing integration: the aiur test toplevels migrate off the
removed builtins (u8_chain_rotr7/4 wrappers and the u32_rotr7 fixture
become u8_xor_split7/4 wrappers and a two-operand u32_xor_rotr7, with
freshly computed vectors), the new xor_split gadget helpers get
clippy-clean byte casts, codegen regenerated, and all 71 kernel FFT
pins + the shard aggregate re-measured (median -0.4%, shard -1.3%).
aiur (cargo + prove + cross agreement), ixvm (pins + parity),
multi-stark and recursive-verifier suites pass; fmt, clippy, deny
clean.
@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur-recursive fresh

@argument-ci-bot

argument-ci-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

!benchmark — main vs 93c2d25

backends: aiur-recursive · envs: InitStd · set: primary · shard: 0 · baseline: fresh (base-SHA run, bencher bypassed)

aiur-recursive · InitStd — main from: base run @ 4e89546 (fresh — bencher bypassed)

3 constants · 3 with regressions · 3 with improvements (|Δ| > 3.0% on any metric).

constant recursive-prove-time (main) recursive-prove-time (PR) Δ% recursive-peak-ram (main) recursive-peak-ram (PR) Δ% recursive-proof-size (main) recursive-proof-size (PR) Δ% recursive-verify-time (main) recursive-verify-time (PR) Δ% recursive-execute-time (main) recursive-execute-time (PR) Δ% recursive-fft-cost (main) recursive-fft-cost (PR) Δ% prove-time (main) prove-time (PR) Δ% proof-size (main) proof-size (PR) Δ% verify-time (main) verify-time (PR) Δ% peak-ram (main) peak-ram (PR) Δ%
String.split 37.940 s 36.223 s -4.5% 🟢 107.39 GiB 102.47 GiB -4.6% 🟢 5.30 MiB 5.27 MiB -0.5% 33.5 ms 32.6 ms -2.6% 5.947 s 6.196 s +4.2% ⚠️ 180.38B 177.01B -1.9% 16.489 s 16.777 s +1.7% 11.21 MiB 11.19 MiB -0.2% 69.4 ms 74.9 ms +7.9% (1.08× slower) ⚠️ 32.75 GiB 32.27 GiB -1.5%
Vector.append 34.245 s 34.186 s -0.2% 95.54 GiB 89.95 GiB -5.8% (1.06× smaller) 🟢 5.30 MiB 5.27 MiB -0.5% 32.7 ms 35.8 ms +9.6% (1.10× slower) ⚠️ 5.193 s 5.231 s +0.7% 157.02B 152.94B -2.6% 4.022 s 4.042 s +0.5% 10.42 MiB 10.39 MiB -0.3% 66.7 ms 64.9 ms -2.8% 7.30 GiB 6.67 GiB -8.7% (1.09× smaller) 🟢
Nat.add_comm 22.120 s 21.069 s -4.8% 🟢 58.69 GiB 56.17 GiB -4.3% 🟢 5.30 MiB 5.27 MiB -0.5% 35.1 ms 32.1 ms -8.5% (1.09× faster) 🟢 3.809 s 3.725 s -2.2% 113.85B 107.33B -5.7% (1.06× fewer) 🟢 1.031 s 1.071 s +3.9% ⚠️ 8.93 MiB 8.90 MiB -0.3% 53.5 ms 55.8 ms +4.3% ⚠️ 3.57 GiB 3.80 GiB +6.3% (1.06× larger) ⚠️

Workflow logs

@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur fresh

@argument-ci-bot

argument-ci-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

!benchmark — main vs 93c2d25

backends: aiur=prove · envs: InitStd · set: primary · shard: 0 · baseline: fresh (base-SHA run, bencher bypassed)

aiur · InitStd · prove — main from: base run @ 4e89546 (fresh — bencher bypassed)

13 constants · 4 with regressions · 12 with improvements (|Δ| > 3.0% on any metric).

comparison table (13 constants)
constant prove-time (main) prove-time (PR) Δ% throughput (const/s) (main) throughput (const/s) (PR) Δ% peak-ram (main) peak-ram (PR) Δ% execute-time (main) execute-time (PR) Δ% verify-time (main) verify-time (PR) Δ% proof-size (main) proof-size (PR) Δ% fft-cost (main) fft-cost (PR) Δ%
Array.extract_append 38.412 s 40.725 s +6.0% (1.06× slower) ⚠️ 40.140 37.860 -5.7% (1.06× slower) ⚠️ 73.74 GiB 73.20 GiB -0.7% 9.762 s 9.839 s +0.8% 131.2 ms 171.2 ms +30.5% (1.31× slower) ⚠️ 21.59 MiB 21.54 MiB -0.2% 136.02B 135.19B -0.6%
ByteArray.utf8DecodeChar?_utf8EncodeChar_append 40.192 s 36.951 s -8.1% (1.09× faster) 🟢 66.950 72.830 +8.8% (1.09× faster) 🟢 73.13 GiB 72.14 GiB -1.3% 9.854 s 9.761 s -0.9% 208.4 ms 142.0 ms -31.9% (1.47× faster) 🟢 21.69 MiB 21.63 MiB -0.2% 138.80B 136.87B -1.4%
Char.ofOrdinal_le_of_le 31.295 s 30.851 s -1.4% 84.550 85.770 +1.4% 58.55 GiB 58.02 GiB -0.9% 6.842 s 6.596 s -3.6% 🟢 137.9 ms 147.5 ms +7.0% (1.07× slower) ⚠️ 21.64 MiB 21.58 MiB -0.2% 99.42B 98.00B -1.4%
Vector.extract_append._proof_2 22.879 s 21.115 s -7.7% (1.08× faster) 🟢 56.910 61.660 +8.3% (1.08× faster) 🟢 39.69 GiB 39.42 GiB -0.7% 5.403 s 5.145 s -4.8% 🟢 143.7 ms 136.6 ms -4.9% (1.05× faster) 🟢 21.30 MiB 21.24 MiB -0.2% 77.08B 76.48B -0.8%
_private.Init.Data.Range.Polymorphic.SInt.0.Int64.instRxcHasSize_eq 18.705 s 17.135 s -8.4% (1.09× faster) 🟢 96.870 105.750 +9.2% (1.09× faster) 🟢 34.76 GiB 34.28 GiB -1.4% 3.736 s 3.563 s -4.6% 🟢 147.8 ms 135.4 ms -8.4% (1.09× faster) 🟢 21.47 MiB 21.42 MiB -0.2% 54.85B 54.00B -1.5%
String.split 17.152 s 16.099 s -6.1% (1.07× faster) 🟢 102.910 109.640 +6.5% (1.07× faster) 🟢 32.73 GiB 32.23 GiB -1.5% 3.316 s 3.307 s -0.3% 135.6 ms 134.7 ms -0.7% 21.68 MiB 21.62 MiB -0.2% 50.25B 49.51B -1.5%
List.mergeSort 12.693 s 11.910 s -6.2% (1.07× faster) 🟢 114.080 121.580 +6.6% (1.07× faster) 🟢 23.78 GiB 23.50 GiB -1.1% 2.440 s 2.416 s -1.0% 132.8 ms 131.0 ms -1.3% 21.53 MiB 21.48 MiB -0.2% 36.10B 35.52B -1.6%
Vector.append 4.202 s 3.936 s -6.3% (1.07× faster) 🟢 115.410 123.230 +6.8% (1.07× faster) 🟢 6.88 GiB 6.46 GiB -6.2% (1.07× smaller) 🟢 695.0 ms 665.7 ms -4.2% 🟢 135.9 ms 123.8 ms -8.9% (1.10× faster) 🟢 20.13 MiB 20.08 MiB -0.3% 8.02B 7.90B -1.5%
Nat.gcd_comm 3.404 s 3.154 s -7.4% (1.08× faster) 🟢 114.560 123.670 +8.0% (1.08× faster) 🟢 5.77 GiB 4.93 GiB -14.6% (1.17× smaller) 🟢 540.1 ms 512.6 ms -5.1% (1.05× faster) 🟢 133.1 ms 118.1 ms -11.2% (1.13× faster) 🟢 19.90 MiB 19.84 MiB -0.3% 5.25B 5.15B -1.9%
String.append 2.561 s 2.369 s -7.5% (1.08× faster) 🟢 118.690 128.340 +8.1% (1.08× faster) 🟢 4.99 GiB 5.49 GiB +10.0% (1.10× larger) ⚠️ 416.6 ms 395.5 ms -5.1% (1.05× faster) 🟢 128.8 ms 114.0 ms -11.5% (1.13× faster) 🟢 19.17 MiB 19.12 MiB -0.3% 2.90B 2.85B -1.9%
Int.gcd 2.109 s 1.976 s -6.3% (1.07× faster) 🟢 98.630 105.240 +6.7% (1.07× faster) 🟢 5.15 GiB 4.54 GiB -11.9% (1.13× smaller) 🟢 359.8 ms 344.6 ms -4.2% 🟢 122.5 ms 111.9 ms -8.7% (1.10× faster) 🟢 18.73 MiB 18.67 MiB -0.3% 1.87B 1.83B -1.8%
Nat.sub_le_of_le_add 1.943 s 1.820 s -6.3% (1.07× faster) 🟢 87.510 93.400 +6.7% (1.07× faster) 🟢 4.98 GiB 4.50 GiB -9.7% (1.11× smaller) 🟢 355.1 ms 333.4 ms -6.1% (1.07× faster) 🟢 119.2 ms 120.7 ms +1.3% 19.08 MiB 19.03 MiB -0.3% 1.60B 1.58B -1.7%
Nat.add_comm 1.132 s 1.034 s -8.7% (1.10× faster) 🟢 37.090 40.620 +9.5% (1.10× faster) 🟢 3.76 GiB 4.12 GiB +9.6% (1.10× larger) ⚠️ 265.4 ms 242.9 ms -8.5% (1.09× faster) 🟢 108.5 ms 108.5 ms -0.0% 17.25 MiB 17.20 MiB -0.3% 271.37M 268.28M -1.1%

Workflow logs

@gabriel-barrett gabriel-barrett changed the title perf/virtual rotr chain output perf/virtual xor split Aug 13, 2026
@gabriel-barrett
gabriel-barrett force-pushed the perf/virtual-rotr-chain-output branch from 4e87352 to 67ada28 Compare August 14, 2026 11:31
@gabriel-barrett
gabriel-barrett force-pushed the perf/virtual-rotr-chain-output branch from 67ada28 to ac1cc6d Compare August 14, 2026 11:32
Comment thread crates/aiur/src/lib.rs Outdated
Comment on lines +84 to +89
pub fn u8_xor_split7_channel() -> G {
G::from_u8(15)
}

#[inline]
pub fn u8_chain_rotr4_channel() -> G {
G::from_u8(14)
pub fn u8_xor_split4_channel() -> G {
G::from_u8(16)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to skip 13 and 14

The add-then-remove sequence had left u8XorSplit7/4 in different slots
than u8ChainRotr7/4 occupied, scrambling the diff and the FFI tag
order. Now the new ops sit exactly where the old ones were: same
Bytecode.Op constructor tags (24/25, with the FFI decoder renumbered
to match), same lookup channels (13/14), and the same positions in
every enum, match, and gadget table. The u32-add ops stay appended at
the end (tags 31-33). Codegen regenerated; all FFT pins unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants