Skip to content

Release 0.6.1 with non-search packed performance improvements - #22

Merged
jcfangc merged 51 commits into
mainfrom
integration/pre-search
Oct 20, 2026
Merged

jcfangc merged 51 commits into
mainfrom
integration/pre-search

Conversation

@jcfangc

@jcfangc jcfangc commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

This integrates the 49 reviewed non-search optimization commits through 6e3c688, followed by workspace version bump dc8e55a to 0.6.1. It improves packed construction, access, owned and borrowed iteration, conversion, fixed-width editing, batched extension, and word-shift backends. Subsequent find/rfind/contains experiments remain on their existing branches.

Both workspace crates inherit version 0.6.1, and Cargo.lock is updated consistently.

Validation:

  • Read-only integration-boundary review: PASS.
  • Staged version-bump review: PASS (gpt-6-luna, high).
  • cargo fmt --check: PASS.
  • Baseline RUSTFLAGS='' cargo test --workspace --offline: PASS (656 library tests plus 149 integration, 100 derive unit and 23 derive integration tests).
  • Native RUSTFLAGS='-C target-cpu=native' cargo test --workspace --locked --offline: PASS (679 library tests plus the same workspace tests).
  • Baseline RUSTFLAGS='' cargo test --workspace --release --locked --offline: PASS (653 library tests plus the same workspace tests).
  • Baseline RUSTFLAGS='' cargo check --benches --locked --offline: PASS.

The PR gate must pass baseline, SSE4.1, AVX2 and ARM64 NEON on the final revision. Merging the Cargo.toml version change into main triggers the existing real release workflow, publishing derive first and then bit-string. This PR does not include changes to the release pipeline.

jcfangc added 20 commits October 1, 2026 23:05
Move the suffix once per packed character through internal BitString
insert_bits/remove_bits primitives. Reuse WordsArith in-place shifts,
preserve the boundary prefix, and keep padding and lazy shrinking intact.

Add bit/character model tests and editing benchmarks at lengths
63/64/65/127/128/129 for BITS=1..8.

Validation: default, native and release tests; fmt and staged diff checks;
characterization bench check; read-only gpt-6-luna/high review.

Native 1M middle-edit medians in microseconds (before -> after):
BITS  insert          remove
1     16.38 -> 1.37    12.21 -> 1.16
2     53.08 -> 2.87    58.67 -> 2.51
3     113.0 -> 3.46    144.8 -> 3.21
4     194.4 -> 5.64    262.1 -> 5.48
5     307.0 -> 29.77   424.9 -> 7.69
6     423.8 -> 37.32   608.9 -> 8.93
7     614.1 -> 48.78   860.2 -> 10.64
8     806.8 -> 58.21   1106  -> 11.74

Compared with 51df6fa using identical benchmark sources, 100 samples and
isolated Cargo target directories. Some short BITS=1 cases regress by
approximately 2-9 ns; retain the uniform path and disclose this tradeoff.
For exact-overlap shifts of 1..63 bits, preserve the outgoing carry before
writing each word and walk the input forward. This shape lets LLVM vectorize
the carry stream in the AVX2 backend. Keep the existing implementation for
independent destinations, zero, whole-word and mixed shifts.

Document the overlap proof beside the unsafe loop, update traversal comments,
and cover owned/in-place vector boundaries with backend-equivalence tests.
Inspired by grit-bitvec's insertion carry stream.

Add paired ours/grit insertion benchmarks for reserved capacity, repeated
insertion with natural growth, and repeated insertion with reserved capacity.
Each batch inserts min(initial_length, 256) values into the same object;
input preparation and reservation are outside timing.

Two CPU-0-pinned native A/B rounds at 1,048,576 initial characters show about
21-44% lower time for 256 consecutive insertions across BITS=1..8. Whole-batch
median ranges in milliseconds:

BITS    baseline        carry
1       0.423-0.426     0.245-0.246
2       0.845-0.878     0.492-0.500
3       1.310-1.312     0.762-0.835
4       1.683-1.752     0.984-0.993
5       2.171-2.209     1.266-1.409
6       2.593-2.655     2.046-2.072
7       2.995-3.100     2.320-2.401
8       3.390-3.511     2.515-2.729

Same-process rotating kernel comparisons also favor this form over the
original loop, a 16-word unroll, and an indexed carry loop. Only this candidate
is retained. Allocation-inclusive owned shift measurements remain noisy;
isolated independent-destination comparisons show no consistent large
regression. Frequency and background load were not controlled.

Validation: cargo fmt --check; git diff --cached --check; default tests
(653 library + 149 integration + 100 packed); native tests (677 + 149 + 100);
3 native release shl-core tests; cargo check --benches --features
characterization; default/native insertion benchmarks and native shift checks.
For exact-overlap right shifts of 1..63 bits, cache the current word and read
its successor before overwriting it. Continue low to high and shift the final
word without a successor. Document the pointer bounds and old-word invariant.
Keep the existing owned, zero, whole-word and mixed-shift paths.

Extend the wide backend-equivalence checks to SSE2/AVX2/NEON for both shift
operations. Register bit_ops_shr_assign with single assign, owned output and
256 shifts of the same allocation, at 4096/65536/1048576 bits and shifts 1/17/65.
Input preparation is outside timing; output allocation is included for owned.

Portability experiments, rustc 1.98.1 on x86-64:
- SSE2-only (target-cpu=x86-64): shl forward carry showed no stable large-input
  win, and shr reverse carry/forward lookahead regressed. Retain both original
  SSE2 kernels and record the negative decisions beside them.
- Native/AVX2: mirrored reverse carry regressed shr. Forward word lookahead
  improved large in-place shr kernels by about 20-32% in rotating same-process,
  CPU-0-pinned comparisons. Small inputs fluctuated; this is not a uniform win
  claim for every length. Independent destinations retain the old path.
- NEON: no AArch64 target, toolchain or native runtime available. Algorithms
  remain unchanged; the expanded equivalence checks will apply on ARM builds.
  SSE2 functions compiled with target-cpu=native can inherit AVX2, so those
  measurements were not treated as evidence of SSE2-only performance.

Native 1M-character single PackedString removal medians (microseconds):
BITS    before    after
1        1.390    0.961
2        2.561    2.538
3        4.718    2.929
4        6.287    4.993
5        7.309    5.896
6        9.435    7.551
7       10.060    8.072
8       11.760    9.214

These end-to-end runs were separate and unpinned; treat small differences as
noise. The pinned rotating kernel experiments separately reproduced the large
sub-word-shift improvement. Whole-word/mixed controls were included.

Validation: default tests (653 library + 149 integration + 100 packed), native
tests (676 + 149 + 100), native release shift-core tests, fmt/diff checks,
characterization bench compilation. Standalone native and SSE2-only comparisons
checked lengths 0..80 and shifts 0..130/191/256/4096, owned/in-place and guard
words. Only the beneficial AVX2 forward-lookahead prototype is retained.
Record why the rustc 1.98.1 SSE2-only carry experiments were not retained:
left shift traded saved loads for copies/shuffles without a throughput win;
right shift's scalar lookahead compiled to one SHRD per word and lost the
existing kernel's two-word SIMD parallelism. Scope these findings to the
measured prototypes rather than claiming an inherent SSE2 limitation.

Comments only. Validation: cargo fmt --check and git diff --cached --check.
Keep two original words in a cached SSE2 vector, load the next pair before
writing, and use SHUFPD immediate 1 to select [current.high, next.low]. Shift
both lanes and retain the prefetched pair for the next iteration. Handle the
final two or three words explicitly without overreading. Document the overlap
invariant and lane selection. Add the exact two-word boundary to equivalence
coverage.

Restrict the candidate to exact overlap, 1..63-bit shifts and at least two
words. Preserve the existing independent-destination, zero, whole-word and
mixed-shift paths. No public API, width-specialized policy or dispatch change.
The preceding 30591e9 separately records why scalar carry prototypes failed.

SSE2-only validation uses target-cpu=x86-64, not native: target_feature(sse2)
functions in a native build may otherwise inherit AVX2. Same-process rotating
kernel comparisons pinned to CPU 0 showed roughly 4-8% less time for large
in-place shifts across two runs. A four-vector unroll did not add stable gains
and was discarded.

Two 200-sample project A/B rounds in before/after/after/before order, CPU 0:
1M-character single PackedString removal medians, microseconds:

BITS    before range       candidate range
1       2.041-2.047        1.882-1.883
2       4.007-4.008        3.802-3.807
3       5.868-5.987        5.581-5.712
4       8.040-9.154        7.434-7.436
5       9.939-10.130       9.284-9.519
6      11.830-11.960      11.370-11.460
7      13.890-13.910      13.090-13.260
8      15.770-16.130      14.890-15.380

Most widths improve by about 3-8%. This is a modest gain, not an AVX2-sized
speedup. Allocation-inclusive owned and some single-operation control cases
fluctuated; isolated kernels and repeated control shifts did not show a
consistent regression. No universal cross-platform/workload claim is made.
The candidate stays on the experiment branch for independent review.

Validation: full SSE2-only and native tests, SSE2 release shr-core test,
characterization bench compilation, fmt and cached diff checks. Standalone
harness checks lengths 0..80, shifts 0..130/191/256/4096, independent/exact-overlap
buffers, guard words and a one-u64 vector offset. It passed before timing.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T13:40:05.889611Z dc8e55a PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@codspeed

codspeed Bot commented Oct 20, 2026

Copy link
Copy Markdown

Too many benchmarks in a single upload

The performance report could not be generated because there were too many benchmarks in a single upload to CodSpeed. We recommend sharding your benchmarks into smaller uploads, with max 1000 benchmarks per upload. See the documentation for more information.

@jcfangc
jcfangc merged commit 20c11b6 into main Oct 20, 2026
6 of 7 checks passed
@jcfangc
jcfangc deleted the integration/pre-search branch October 20, 2026 13:55
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