Avoid materializing unions in OrCardinality - #563
Open
gitRasheed wants to merge 2 commits into
Open
Conversation
The per-container orCardinality methods have existed since 8a91ca4 but Bitmap.OrCardinality still built the union and read its cardinality, in both the 32-bit and the 64-bit API.
len(a) + len(b) - |a ∩ b| reuses intersection2by2Cardinality, which already gallops on skewed sizes, and removes the separate merge loop.
gitRasheed
force-pushed
the
perf/or-cardinality
branch
from
September 5, 2026 19:50
d6a8086 to
ed66117
Compare
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.
Description
OrCardinalitybuilt the union container for each matching key pair and read its cardinality. It now calls the count-only container methods, present since 2017 (#40, #46) but never called. The array-array case becomeslen(a) + len(b) - intersection2by2Cardinality(a, b), which already gallops on skewed sizes.Type of Change
Changes Made
What was changed?
roaring.go,roaring64/roaring64.go:OrCardinalitycallsorCardinalityon the containers instead ofor(...).getCardinality().setutil.go:union2by2Cardinalityuses the formula above; the merge loop is gone.Why was it changed?
Materializing the union allocated up to 21 MB per pass over a dataset for a number the existing methods compute in place.
How was it changed?
Two call sites and one function body. No new algorithm, assembly or API.
Testing
go test ./...passes on x86-64 and on c7g.xlarge, c8g.xlarge and c9g.xlarge. A differential test againstOr().GetCardinality()on 38,000 random bitmap pairs across all container types, with and withoutRunOptimize, found no mismatch.Formatting
go fmt,make unconvertandgit diff --checkclean.Fuzzing
smat fuzzer, 300 seconds: 553,072 executions, no failures. The smat model checks
OrCardinalityagainst its reference.Performance Impact
BenchmarkRealDataOrCardinalityruns all 12 real datasets, as loaded and afterRunOptimize, on c7g.xlarge, c8g.xlarge and c9g.xlarge: ten one-second samples per case, Go 1.26.2, one pinned CPU, variant order balanced. One op is a pass over a dataset's adjacent bitmap pairs. Time reduction against master, geometric mean over datasets:All 72 instance/dataset/mode cases are faster at p < 0.001, and all allocate nothing where master allocated up to 21 MB per op. The smallest gain is
uscensus2000, which has almost no array containers. The largest iscensus1881afterRunOptimize, 5.32 ms to 32.2 µs, where master built 19 MB of temporary containers per pass.c8g.xlarge, every dataset
Medians in µs/op. The last two columns are what master allocated per op; this branch allocates nothing.
Most datasets have 199 pairs per op;
dimension_003has 15,481,dimension_0085,239,dimension_033172. Base65fb6f8,real-roaring-datasets929d808.Container pairs,
BenchmarkOrCardinalityContainersandBenchmarkOrCardinalityArrayon c8g.large, six 300 ms samples per case, oneOrCardinalitycall over four aligned containers per op: