Repository navigation
Conversation
tamirms
added a commit
to stellar/stellar-rpc
that referenced
this pull request
Sep 12, 2026
eval narrowed a per-filter accumulator through the filter's groups one AndAny at a time, so every filter paid an 8 KiB accumulator on every slab even when its groups had nothing in common there, and a topic-count range paid its union on top. A filter now yields plans that are conjunctions of single terms, one per bucket of a range, since A and (b or c) is (A and b) or (A and c) and the union across plans keeps the or; the planner drops a plan that repeats an earlier one. The slab engine then holds one bitmap per term, runs one FastAnd over the slab's id range and the terms per plan, keeps its bounds as the searches return them, and counts each term's cardinality once. The slab bitmap is built once per slab and shared by every plan. With the pinned roaring, FastAnd intersects pairwise and the cost is unchanged or lower. With roaring's count-first intersections (RoaringBitmap/roaring#566 and the FastAnd follow-up built on it) an empty result allocates nothing, which is what bounds the slab walk on filter lists that match nothing; the version bump will add the contract test for that. roaring_contract_test.go now pins that FastAnd leaves its inputs untouched, returns fresh containers on the two- and many-input paths over whole and partial slab ranges, and agrees with the pairwise And chain for every subset of the container kinds; that a slab range built with AddRange is stored as run containers; and runs FastAnd in the concurrent-readers race gate. The AndAny pins go, since the engine no longer calls it. The shaped differential matrix gains a filter with a term and a count range, and a planner test covers the fan-out and the dedup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M23ZvEkyrFjyUUbm7zLwob
tamirms
force-pushed
the
pr1-run-and-count-first
branch
2 times, most recently
from
September 15, 2026 05:44
2a85570 to
3fe980c
Compare
This was referenced Sep 17, 2026
tamirms
force-pushed
the
pr1-run-and-count-first
branch
3 times, most recently
from
October 9, 2026 08:28
d1277dc to
d5e3273
Compare
…ions
Intersecting a run container with a bitmap built an 8 KiB bitmap
container before knowing whether the result was empty, a small array or
a bitmap, and runContainer16.intersects built a whole container to
answer a boolean.
andBitmapContainer and iandRun16 now count the result first, with the
existing andBitmapContainerCardinality, and build only what is returned;
in place, the gaps between intervals are cleared directly. A run with
more than 64 intervals goes through a stack scratch bitmap instead when
it covers more than 4096 values or the CPU has the VBMI2 vector fill.
intersects returns at the first shared value. An in-place And with a
full range returns the container unchanged, and resetBitmapRange clears
whole words with clear.
Xeon 8375C, BenchmarkRunAndBitmap (added):
And, empty result 2.8 µs -> 594 ns (4.7x)
And, bitmap result 3.7 µs -> 2.0 µs (1.9x)
in-place And, bitmap result 2.2 µs -> 475 ns (4.6x), no allocation
Intersects, disjoint 2.8 µs -> 118 ns (23x), no allocation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198cmGsvonq19wTyzKBsw2Z
tamirms
force-pushed
the
pr1-run-and-count-first
branch
from
October 9, 2026 09:21
d5e3273 to
1c0052a
Compare
Contributor
Author
tamirms
added a commit
to stellar/stellar-rpc
that referenced
this pull request
Oct 24, 2026
* events, deps: memory-map the cold MPHF and overlap cold reads, roaring v2.26.0 Each cold events read opened its chunk's index.hash by reading the whole file into memory. It now memory-maps the file instead. The pages come from the kernel's page cache, which every reader of the chunk shares, so an open costs a map plus the pages its lookups touch, rather than a copy of a file that grows with the chunk's term count. LookupKeys now resolves all of a query's terms with one streamhash QueryBatch call (stellar/streamhash#24) instead of one lookup per term. On a cold page cache a lookup reads a few index.hash pages one after another; QueryBatch requests every term's pages together, so a query's lookups cost about one round of reads instead of one per term. On gp3 with the file evicted from the page cache, opening a 60M-term index and looking up 15 terms took 2.5 ms instead of 17.3 ms at p50, and 5 terms 2.3 ms instead of 6.8 ms. Cold events readers were opened with a concurrency of 0, so each lookup and fetch issued its index.pack and events.pack reads one after another: one per term for a lookup, and one per batch of a page's scattered events for a fetch. The resolver now opens them with a concurrency of 8, which packfile's ReadItems uses for both packs. Measured on gp3 with a cold page cache, with index.pack's big terms split by slab (a later change in this stack), lookups of several terms got 40 to 62% faster at p50: five popular terms took 1.7 ms instead of 4.4 ms on pubnet chunk 6465, and 1.9 ms instead of 4.6 ms on a chunk folded to seven times its density. Single-term lookups, and lookups on a volume already at its throughput limit, are unchanged. roaring moves from v2.18.2 to v2.26.0, which adds vectorized container kernels. Its serialized form is roaring's portable format, so existing artifacts read the same. go.mod replaces it with the head of RoaringBitmap/roaring#572, which sits on RoaringBitmap/roaring#566, until a release includes both: they count or test before allocating in FastAnd and in run-container intersections, so an intersection that comes out empty allocates nothing. Claude-Session: https://claude.ai/code/session_01QHnF5BhsuoxpWxGmatQzmt Claude-Session: https://claude.ai/code/session_01JCzMKKrK3NstGKvaxsHhL2 Claude-Session: https://claude.ai/code/session_01Kkry5utNweWZqUKL88vJ2X Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * deps: pin roaring at the new head of #572 The new head of RoaringBitmap/roaring#572 looks up a one-key call's key in every input, up to eight, before it allocates anything. So an empty per-slab intersection allocates only its result bitmap, whatever mix of array, bitmap and run containers its terms hold. roaring_contract_test.go pins the roaring properties the event index relies on, written against roaring alone so it runs first on any version bump: FastAnd and the aggregations read their arguments and return containers that share no storage with them, the value searches are read-only and inclusive, and an empty FastAnd allocates only its result. That last test makes the one-key call with every mix of array, bitmap and run terms up to four terms and a sample of mixes up to seven, on a whole and a partial slab, with the result emptied at each term in turn. It fails on the previous pin, 64285f3, in 3,263 of its 3,727 cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wwsd8qTEGeSb7poFEYcgg9 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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
Several run-container intersections build an 8 KiB bitmap before they know whether the result is empty, a small array or a bitmap. This counts or tests first, then allocates only what is returned.
Type of Change
Changes Made
What was changed?
runContainer16.andBitmapContainer(run AND bitmap) counts the result first, then builds an empty array, an exact-size array or a bitmap.bitmapContainer.iandRun16(in-place bitmap AND run) counts the same way. A small result becomes an array; otherwise the gaps between the run's intervals are cleared in place.Andwith a full range returns the container unchanged. A bitmap used to be copied, and a run intersected.runContainer16.intersectsreturns at the first shared value instead of building a container to answer a boolean.resetBitmapRangeclears whole words withclearinstead of one at a time. The in-place path above uses it for every gap.clearBitmapGapsandcontainerFromWords, andrunbitmap_and_test.gowith tests andBenchmarkRunAndBitmap.Why was it changed?
Intersectsallocated a container to answer a boolean. Every mixedIntersectscall that touches a run goes through it.How was it changed?
bitmapContainer.andBitmapalready works.bitmapContainer.andBitmapdoes.intersectsscans masked words for bitmaps, merges likeandArrayfor arrays (or searches per value when the intervals far outnumber the values), and tests interval overlap for runs.Testing
runbitmap_and_test.gochecks each path against its old definition,newBitmapContainerFromRun(rc).andBitmap(bc)and!rc.and(c).isEmpty(), over random container pairs.Andwith a full range allocate no container.All pass on linux/amd64 (Xeon 8375C, with AVX-512 VBMI2), darwin/arm64, and amd64 without AVX-512.
Formatting
gofmt -s -l .andgo tool unconvert ./...report nothing.go vet ./...reports only the existingReadFromsignature warning from master.Fuzzing
The
FuzzSmattarget in the template does not exist on master (smat.gorefers to a missingsmat_fuzz_test.go), so-fuzz=FuzzSmatmatches nothing. The smat corpus runs and passes, but none of its sequences reaches these functions:The randomized comparison under Testing is what exercises the change.
Performance Impact
BenchmarkRunAndBitmap(added) intersects a run-optimized bitmap with a dense one, over one key. The in-place cases use a fresh copy each time, made outside the timer; the "copy +" cases include the copy. Xeon 8375C:And, empty resultAnd, array resultAnd, bitmap resultAnd, 1024-interval run, array resultAnd, 1024-interval run, bitmap resultAnd, bitmap resultAnd, 1024-interval runAnd, bitmap resultAnd, 1024-interval runIntersects, disjointIntersects, overlappingIntersects, 3 values vs 1024 intervalsPerformance Analysis
Andno longer allocates: 2.2 µs to 472 ns with a bitmap result.BenchmarkIntersection*,BenchmarkUnion*andBenchmarkAndAny/*are unchanged.Breaking Changes
None.
Related Issues
None.
Additional Notes
bitmapContainer.iandArrayconverts the array to a bitmap the same way; that is left for a separate change.🤖 Generated with Claude Code