Leverage sorted dim when visiting BKD leaf. - #12528
Conversation
|
@jpountz Please take a look when you get a chance! |
|
@iverase I replaced int values with static variables. Please take a look. |
| public static final int HIGH_IN_SORTED_DIM = 2; | ||
| /** Packed value is too high in NON-SORTED dimension */ | ||
| public static final int HIGH_IN_NON_SORTED_DIM = 3; | ||
| } |
There was a problem hiding this comment.
My main concern here is that the concept of SORTED dimension does not exist in the PointValues API. If you have a look to the javadocs when visiting a leaf node:
/**
* Called for all documents in a leaf cell that crosses the query. The consumer should
* scrutinize the packedValue to decide whether to accept it. In the 1D case, values are visited
* in increasing order, and in the case of ties, in increasing docID order.
*/
It only constraints the 1D case but in higher dimensions there is no constraint how data is visited. The concept of SORTED dimension sounds to me an implementation detail that should not be leaked to the public API.
iverase
left a comment
There was a problem hiding this comment.
I have concerns with this change as I have the impression that we are leaking implementation details into the public API which is not a good idea in general. Maybe other folks have a different opinion but I don't think the leaves sorted dimensions should be part of the public API, or at least we need to make it explicit before this change.
…ator, byte[]) to make other queries use DocIdSetIterator.
|
@iverase Does it make sense to you if MatchState defined in other class, such as BKDReader or IntersectVisitor, and only leave the sorted dimension in IntersectVisitor' visit method as a parameter? |
|
My recommendation is to use the following method as you are just trying to flag if the visit method needs to keep processing points. I would remove the "inverse" case as the inverse visitors are implementation details and IMHO should not be part of the API.
We should run the performance test in the [lucene benchmarks](lucene benchmarks) to check if there are slowdowns due to the extra check for each visited point. |
|
@iverase Thanks for your recommendation, it may makes code more clear. |
|
@iverase Here is a performance test from luceneutil.
I only implemented visitWithSortedDim in PointRangeQuery (without "inverse" case) temporarily, so I just ran the box's points test.
Do you mean the off the shelf benchmark in lucene self? |
One case I am interested in is the geo benchmarks. It is not clear to me if some of those queries (e.g polygon query) can effectively take advantage of this change so it would be good to know that they are not slowed down. |
|
@iverase
I want to add a enhanced IntersectVisitor type or add a flag to IntersectVisitor, for queries which have already implemented visitWithSortedDim. And leave queries which haven't or couldn't implement visitWithSortedDim, use the original visit method to avoid the unnecessary checks. |
|
@iverase I implemented visitWithSortedDim in LatLonPointDistanceQuery. Please take a look when you get a chance! BTW, The contents of getIntersectVisitor and getInverseIntersectVisitor in LatLonPointDistanceQuery and PointRangeQuery are identical. Should we move them to a DefaultIntersectVisitor and make match/relate abstract in it. I mean in a new PR without this change. |
|
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the dev@lucene.apache.org list. Thank you for your contribution! |
FWIW, I implemented "inverse" case with adding a |
|
I will try to measure the performance. |
|
Performance with "inverse" visitor implemented:
|
|
@jpountz Please take a look when you get a chance. |
|
Performance details: baseline: candidate: |
|
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the dev@lucene.apache.org list. Thank you for your contribution! |
|
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the dev@lucene.apache.org list. Thank you for your contribution! |
There was a problem hiding this comment.
Pull request overview
This PR optimizes BKD leaf visits by leveraging the fact that leaf values are sorted on a “sorted dimension”, enabling early termination (or bulk matching of remaining docs for inverse queries) when the current value is beyond the query’s upper bound on that dimension.
Changes:
- Add
VisitState/MatchStateand newIntersectVisitor#visitWithSortedDim(...)hooks to support early-termination semantics. - Update BKD leaf decoding to call
visitWithSortedDim(...)for both sparse (low-cardinality) and compressed (high-cardinality) leaves. - Extend point query implementations and add new tests to cover sparse/compressed scenarios.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| lucene/test-framework/src/java/org/apache/lucene/tests/index/AssertingLeafReader.java | Adds assertions for new visitWithSortedDim APIs (but currently weakens docBudget validation for bulk paths). |
| lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java | Adds new tests covering sparse/compressed leaf behavior for the new visitor mode. |
| lucene/core/src/java/org/apache/lucene/util/bkd/BKDWriter.java | Writes additional metadata for low-cardinality leaves to support sorted-dim visiting. |
| lucene/core/src/java/org/apache/lucene/util/bkd/BKDReader.java | Reads new metadata and performs early-termination-aware visiting while decoding leaf values. |
| lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java | Implements early termination/match-remaining behavior for point range queries. |
| lucene/core/src/java/org/apache/lucene/index/PointValues.java | Introduces new enums and default visitor methods to support sorted-dim visiting. |
| lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java | Implements early termination/match-remaining behavior for distance queries based on sorted dim. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| out.writeByte((byte) -2); | ||
| out.writeByte((byte) sortedDim); |
| if (sortedDim == -2) { | ||
| sortedDim = in.readByte(); |
| break; | ||
| } else if (visitState == VisitState.MATCH_REMAINING) { | ||
| scratchIterator.reset(i, count - i); | ||
| visitor.visit(scratchIterator); | ||
| break; | ||
| } | ||
| i += length; | ||
| } |
| break; | ||
| } | ||
| i += runLen; | ||
| } |
| // TODO: docBudget -= iterator.length. | ||
| // assert --docBudget >= 0 : "called add() more times than the last call to grow() | ||
| // reserved"; |
| public void testCompressedWithSoredDimIntersectVisitor() throws Exception { | ||
| Directory dir = newDirectory(); | ||
| IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(new MockAnalyzer(random()))); | ||
|
|
| public void testCompressedWithSoredDimInverseIntersectVisitor() throws Exception { | ||
| Directory dir = newDirectory(); | ||
| IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(new MockAnalyzer(random()))); | ||
|
|
| MATCH_REMAINING | ||
| }; | ||
|
|
||
| /** Math states for current value. */ |
| /** | ||
| * Similar to {@link IntersectVisitor#visit(int, byte[])} but data is visited in increasing | ||
| * order on the sortedDim, and in the case of ties, in increasing docID order. Implementers can | ||
| * stop processing points on the leaf by returning false when for example the sorted dimension | ||
| * value is too high to be matched by the query. | ||
| * | ||
| * @return VisitState.CONTINUE if the visitor should continue visiting points on this leaf, | ||
| * otherwise false. | ||
| */ | ||
| default VisitState visitWithSortedDim(int docID, byte[] packedValue, int sortedDim) | ||
| throws IOException { | ||
| visit(docID, packedValue); | ||
| return VisitState.CONTINUE; | ||
| } |
| // // cross | ||
| // if (lon > maxLon && lon < minLon2) {} | ||
| // // disable | ||
| // if (lon > maxLon || lon < minLon) {} |
|
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the dev@lucene.apache.org list. Thank you for your contribution! |
Since values are sorted on the sorted dim in BKD leaf, early terminate the visit when current packed value greater than upper point in sorted dimension, may get a better performance.
Here are some performance data:
1 dim point
Visit method for low cardinality points:
Whole search for low cardinality points:
Whole search for high cardinality points:
2 dim point
Whole search for low cardinality points:
Whole search for high cardinality points: