Skip to content

perf(encode): 1.08-1.27x faster matchfind at L3-L12, byte-identical; rusty_alloc 2.0.5 - #12

Merged
Ttimmahlax merged 5 commits into
mainfrom
perf/matchfind-campaign
Sep 9, 2026
Merged

perf(encode): 1.08-1.27x faster matchfind at L3-L12, byte-identical; rusty_alloc 2.0.5#12
Ttimmahlax merged 5 commits into
mainfrom
perf/matchfind-campaign

Conversation

@Ttimmahlax

@Ttimmahlax Ttimmahlax commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

What this is

The matchfind campaign, measured, plus the allocator seam moved to
rusty_alloc 2.0.5. Compressed output is byte-identical to v0.2.3 at every
level except 3.

The numbers

Every verdict in the changelog is an instruction count, because this box
runs at ~65% load from other work and cannot resolve a stopwatch. This is the
wall-clock check on those counts: one measurement program (speedab) compiled
against both the released v0.2.3 (e672cd0) and this branch, looping
compress in memory with no file I/O in the timed region, whole processes
alternated ABBA and pinned to one core at High priority, estimator the floor.

The instrument's floor, measured first by running a binary against itself:
encode ±3%, decode ±0.5%.
Nothing below that is reported as a result.

L strategy dickens 9.7 MiB samba 20.6 MiB speedup
1 Fast 189.8 → 186.9 272.3 → 275.5 flat (inside the floor)
3 DFast 129.4 → 140.3 261.0 → 268.4 1.08 / 1.03
5 Greedy 67.2 → 79.1 101.8 → 128.7 1.18 / 1.27
7 Lazy 11.0 → 13.4 22.1 → 28.0 1.23 / 1.26
9 Lazy2 18.2 → 20.9 38.0 → 46.0 1.14 / 1.21
12 Lazy2 3.5 → 3.9 12.3 → 13.3 1.11 / 1.08
15 BtLazy2 3.0 → 3.0 xml 8.3 → 8.9 flat / 1.07
19 BtUltra 2.5 → 2.5 xml 5.3 → 5.6 flat / 1.05

The shape is the reason to trust it: the win is exactly where the campaign
worked and absent where it did not.
L5–L12 are the greedy and lazy chain
finders, which took nearly every brick. L1 is the Fast ladder, which took none.
L15–L19 are the tree ladder, which took a single per-call prologue trim.

The allocator, priced honestly

rzstd-alloc moves its exact pin 1.1.42.0.5. Measured on the same
instrument with the same codec on both arms:

workload encode decode sign test
bulk (dickens, samba @ L1/5/9/12) 0.96–1.05 0.97–1.02 17/40, z = −0.95
allocation-heavy (smallmsg, jsonlog @ L1/L3) 1.00–1.02 1.00–1.03 12/28

Every cell is inside the floor and the sign test is a coin flip, so this is
recorded as a dependency-currency update, not a speed win. The
allocation-heavy arm was run separately because bulk codec loops are the wrong
place to judge an allocator; it reads flat too.

Correctness

  • Byte-identity: GOLD 2F6594F7EEDBD12B / 59,680,638 and LDM
    57BE83EA4E1199E8 / 57,796,847 — unchanged after every landed brick and
    again after the allocator bump.
  • Sizes move at L3 only, the one ladder carrying a deliberate bitstream change
    in this release (the next-long offset-trade dispatch defaulting ON): samba
    −1.14% (smaller), dickens +0.04%. That is also why the L3 decode row
    moves — a smaller frame is less work, not a decode win, and is not claimed.
  • Tests pass; clippy -D warnings and fmt --check clean on the three CI
    crates; wasm32 std and alloc checks clean; cargo package -p rusty_zstd
    builds from committed state.
  • New kernel-reach gate in CI counts whether the SIMD kernels are actually
    called, with a poison self-check that must fail. Every slot reads 100.00%.
    It exists because an AVX2 checksum kernel once shipped reachable only from a
    test and a benchmark, and every other gate passed.

CI found two defects, and that is the point

Both files below were untracked before this branch, so no CI had ever
compiled them
. Committing the instruments is what exposed them:

  • rssgrow defined fn rss() under #[cfg(windows)] and called it
    unconditionally — every Linux and macOS build failed with E0425.
  • the kernel-reach gate asserted a flat checked >= 6. Eight of its ten slots
    need BMI2, which no aarch64 host has, so macOS runners skip them by design
    and a correctly-routed build failed. The floor is now host-derived.

Fixed and cross-checked locally against x86_64-unknown-linux-gnu and
aarch64-apple-darwin rather than by another CI round trip.

Note for review

The second commit adds instruments and tools/asmcensus/, and the first adds
two library modules (copies, kreach) plus two gates that the tree was
already using untracked — without them a clean checkout does not build.

Merging this to main is what triggers release-plz to open its release PR.

🤖 Generated with Claude Code

tim-almond-house and others added 5 commits September 9, 2026 14:39
The chain finders' three hot units were priced against the emitted assembly
and worked round-robin: the walk's per-CANDIDATE first-word path, the fill's
per-INSERTED-BYTE loop, and the lazy finder's per-POSITION loop. Every verdict
is a shortest header-to-latch path on the emitted asm, never a stopwatch --
this box runs at ~65% load from other work and cannot resolve one.

The wall-clock check, taken with ONE measurement program compiled against both
the released v0.2.3 and this tree, whole processes alternated ABBA and pinned
to one core, estimator the floor. Instrument floor measured first by running a
binary against itself: encode +-3%, decode +-0.5%.

  L5  Greedy   67.2 -> 79.1 MB/s (dickens)   101.8 -> 128.7 (samba)
  L7  Lazy     11.0 -> 13.4                   22.1 ->  28.0
  L9  Lazy2    18.2 -> 20.9                   38.0 ->  46.0
  L12 Lazy2     3.5 ->  3.9                   12.3 ->  13.3
  L1  Fast     flat -- the ladder that took no bricks
  L15/L19      flat to 1.07 -- one brick, a per-call prologue trim

The win is exactly where the campaign worked and absent where it did not,
which is the reason to trust both the counts and the clock.

Compressed output is byte-identical throughout: GOLD 2F6594F7EEDBD12B /
59,680,638 bytes and LDM 57BE83EA4E1199E8 / 57,796,847, unchanged after every
landed brick. Sizes move at L3 only, where the DFast back-extension trades:
samba -1.14% (smaller), dickens +0.04%.

Also in this commit:

- the allocator seam pin moves 1.1.4 -> 2.0.5. Priced on the same instrument
  and it is NEUTRAL on this workload (bulk 17/40 wins, z = -0.95; an
  allocation-heavy arm run separately because bulk codec loops are the wrong
  place to judge an allocator reads flat too). Recorded as a currency update
  rather than dressed up as a win.
- a kernel-reach gate in CI that COUNTS whether the SIMD kernels are actually
  called, with a poison self-check that must fail. Every slot reads 100.00%.
  The census exists because an AVX2 checksum kernel once shipped reachable
  only from a test and a benchmark, and every other gate passed.
- the library modules the tree was already using untracked (copies, kreach)
  and their gates, so a clean checkout builds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deterministic censuses and the assembly-census tools, kept because a number in
the changelog that nobody can reproduce is an assertion, not evidence. None of
this is in the published crate: `rusty_zstd-bench` is publish = false and CI
checks it with `--all-targets --features profile`.

The ones the changelog names by hand:

- `speedab` -- the A/B measurement program: loops compress/decompress in
  memory with no file I/O in the timed region, best-of-N per run, so the SAME
  source compiled against two library versions is the whole comparison.
- `fillcensus` -- walk exits, candidates examined, tag skips (and false skips,
  which must read 0), fill inserts, and the phantom position-0 census.
- `phantoms` -- candidates examined at position 0 and how many were accepted,
  which is what settled the null-link representation question.
- `tools/asmcensus/` -- the board (`verdict3.py`), the one-number per-byte
  model (`score.py`), the path dumps, and `fillloops.py`, which reads a fill
  body's arms apart when the board's single row folds them together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ible

Both files were untracked before this branch, so no CI had ever compiled them.
Committing them is what exposed the defects -- which is the argument for
tracking instruments rather than leaving them on one machine's disk.

- `rssgrow` defined `fn rss()` under `#[cfg(windows)]` and called it
  unconditionally, so every Linux and macOS build failed with E0425. The body
  is a stub on every platform, so the gate protected nothing. Removed.

- the kernel-reach gate asserted `checked >= 6` exercised sites. Eight of the
  ten slots need BMI2, which no aarch64 host has, so on macOS runners they are
  SKIPPED by design and a perfectly-routed build failed the gate. The floor is
  now derived from the host (6 with BMI2, 2 with only a vector ISA, else 1) so
  it still refuses to pass on silence without asserting an x86 assumption.

  The poison self-check still bites on aarch64: `set_xxh_avx2_arm(false)` gates
  the NEON stripe path as well as the AVX2 one, so `xxh64 stripes` goes scalar
  and the routing assertion fires. `count_eq_len`'s NEON arm is chosen at
  compile time and cannot be poisoned; one poisonable slot is enough, and that
  asymmetry is now written down beside the floor.

Verified by cross-checking rather than another CI round trip: `cargo check
--all-targets --features profile` clean for both x86_64-unknown-linux-gnu and
aarch64-apple-darwin, and locally the gate passes while the poison run fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The entry described the gate as failing "if fewer than 6 sites were
exercised", which is what it did until this branch and what broke every
aarch64 runner. A stale comment beside the thing it documents is the drift
this repository already calls out by name, so the description moves with the
code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed it

The measured section credited the L3 delta to the DFast back-extension, which
shipped in v0.2.3 and therefore cannot explain a difference against v0.2.3. The
cause is the next-long offset-trade dispatch defaulting ON, which this release
already documents at -0.36% across an 18-corpus L3 board.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Ttimmahlax
Ttimmahlax merged commit 85ccb28 into main Sep 9, 2026
8 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 9, 2026
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