Repository navigation
Use a skewed intersection for very unequal array containers - #386
Merged
Merged
Conversation
Kerollmops
requested changes
Oct 26, 2026
Kerollmops
left a comment
Member
There was a problem hiding this comment.
Hey @emschwartz 👋
Thank you for your contribution. It looks very interresting but can you rebase on top of #385 once you rebased this one on top of main, please?
Have a nice day 🌵
`ArrayStore -= &ArrayStore` reserved `min(lhs.len(), rhs.len())` for its output, but a difference can hold up to `lhs.len()` values. The Vec grew to cover it, so results were correct. Reserve `lhs.len()`, as `Sub` already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every caller reserves an upper bound for its output, so the `push` in `visit_scalar` never reallocates, but the compiler cannot see that. The scalar merge loops for `&`, `|`, `^` and `-` therefore carry a call to `grow_one` and reload the Vec's fields from the stack on each write. Asserting `len < capacity` before the push removes the call and lets the loops keep their state in registers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When one array is over 64x smaller than the other, binary searching the larger one beats a linear merge, as CRoaring does in intersect_skewed_uint16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
emschwartz
force-pushed
the
skewed-intersection
branch
from
October 26, 2026 18:35
e775ada to
1f523d4
Compare
The bounds checks cost about 10-35% on very skewed intersections, which remain 3-15x faster than the merge, and remove the only new `unsafe`. The comparison is no longer reliably compiled without branches, so the doc comment no longer claims it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XGMzezWxjMNNrZCr1oDKCz
Merged
via the queue into
RoaringBitmap:main
with commit Oct 27, 2026
759df0f
13 of 14 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses #133. Stacked on #385: only the last commit is new, and the numbers below are measured against that PR.
When one array container is more than 64 times larger than the other,
&,&=andintersection_lennow binary search the larger one instead of merging. This follows CRoaring'sintersect_skewed_uint16, by way of saik0's 2022 port linked in the issue. It searches for four values at once so their loads overlap. A galloping search was tried first and was about 2 to 5 times slower at these sizes.Change in time over 10 interleaved runs of each binary. census1881 is the one dataset with many skewed pairs.
and_skewedis a new bench of 10 or 4000 values against 4000.pairwise_and/ref_refcensus1881pairwise_and/assign_refcensus1881pairwise_and/lencensus1881and_skewed/and10and_skewed/and_assign10and_skewed/intersection_len10The other seven datasets pooled are 1 to 7% faster for
ref_ref, 0.5 to 3% forassign_refand 11 to 13% forlen. Rows that only take the merge path, such asand_skewed4000, move between -5% and +3%; the same binary against itself reaches 4% on a single row. Same machines and toolchains as #385.Things to look at:
simd, where a 3-run check put the break-even against the vector merge at a ratio of 10 to 20.lower_boundsusesget_unchecked. Checked indexing made the skewed rows 10 to 18% slower in an earlier 3-run check.and_skewedis#[inline(never)]. With#[inline],and_skewed/and4000 was 12% [+8, +17] slower on Zen 3 and 10% [+3, +18] on Zen 5, although the merge loop is the only code that row runs.simd, a skewed&=now builds a new Vec instead of filtering in place.is_disjointandis_subsetstill merge.Tests compare the skewed path with the existing merge on edge cases and a proptest, in both argument orders, for all three operations.
Authored with Claude (claude-opus-5-5).