-
Notifications
You must be signed in to change notification settings - Fork 169
Enable optimistic search to memory optimized search. #2933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Enable optimistic search to memory optimized search. #2933
Conversation
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Show resolved
Hide resolved
|
@Vikasht34 @shatejas |
|
Will look into this PR :- Tomorrow Morning |
5b006af to
1e09bf0
Compare
| ); | ||
| } | ||
|
|
||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic has been moved to approximateSearch
| * An immutable, empty {@link BitSet} implementation used to represent | ||
| * the absence of filter bits without incurring null checks or allocations. | ||
| */ | ||
| public static final BitSet MATCH_ALL_BIT_SET = new BitSet() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious why we are not uysing Lucene's MathAllBits or MatcNoBits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could not use both, as it's sub class of Bits while we need BitSet in here. 😵💫
Since optimistic search will call approximateSearch twice, we need to keep BitSet for reusing.
| } | ||
|
|
||
| @Override | ||
| public int length() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we doing any iteration on Bitset , this gooona break if we are doing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only place using this one is when we're getting siblings in nested case.
In there, we don't do iteration.
There are caveats to doing this,
I think 1 is a concern which needs discussion, 2 is manageable with some extra logic to keep behavior consistent, 3 isn't a big deal |
@shatejas from a user perspective all of this is a breaking change if we are making Lucene on Faiss default. So this needs to be documented in the docs to clearly callout the behavior and how to mitigate this. Along with this, we should ensure that older indices are still on the same non memory optimized based search so that upgrades are seamless. We have already seen GH issues in part where changes in range of cosine scores lead to issues with users. #2561 |
...java/org/opensearch/knn/index/query/memoryoptsearch/optimistic/Optimistic2ndSearchUtils.java
Outdated
Show resolved
Hide resolved
| @Setter | ||
| private KnnCollectorManager optimistic2ndKnnCollectorManager; | ||
|
|
||
| public static class OptimisticKnnCollectorManager implements KnnCollectorManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we move this to a separate file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, will update in the next rev.
| public PerLeafResult(final Bits filterBits, final TopDocs result) { | ||
| this.filterBits = filterBits == null ? new Bits.MatchAllBits(0) : filterBits; | ||
| // Indicates whether this result was produced via exact or approximate search. | ||
| private final SearchMode searchMode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the use of this parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimistic search would do deep dive HNSW search with acquired top k results as seeds. And this will not be needed if the results acquired via exact search. Hence having search mode here, and let it bypass the second search if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cases when results will be acquired from exact search is the filters case right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! Whenever running exact search for whatever reason, it will set the mode as EXACT_SEARCH.
If that's the case, then we should not run 2nd search as it's pointless.
| * An immutable, empty {@link BitSet} implementation used to represent | ||
| * the absence of filter bits without incurring null checks or allocations. | ||
| */ | ||
| public static final BitSet MATCH_ALL_BIT_SET = new BitSet() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
| * @return a {@link TopDocs} object containing the top {@code k} approximate search results | ||
| * @throws IOException if an error occurs while reading index data or accessing vector fields | ||
| */ | ||
| public TopDocs approximateSearch( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we are making this function public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this particular function in optimistic second search. Otherwise, if using searchLeaf, then we will end up building filter bitset twice.
|
We are brining in a lot of code from Lucene. Please mention the source of the code for better maintainability. One way I would think is to move the class to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks Good !! Clean Code and Very Concise !! Thanks
e625f04 to
0cc0b54
Compare
| /** | ||
| * A special flag used for testing purposes that forces execution of the second (exact) search | ||
| * in optimistic search mode, regardless of the results returned by the first approximate search. | ||
| * <p> | ||
| * This flag should never be enabled in production; it is intended for testing and debugging only. | ||
| */ | ||
| private static final boolean FORCE_REENTER_TESTING; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets remove this in next iteration of PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed for testing the second reentrance in optimistic search.
It's bit tricky to make the data to ensure there's at least one segment whose min topk score is greater than the minimum score in the merged top-k results
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me.
I am not seeing how this new memory optimized search is getting integrated with the explain api.
Also let put the destination branch as main branch.
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
| public PerLeafResult(final Bits filterBits, final TopDocs result) { | ||
| this.filterBits = filterBits == null ? new Bits.MatchAllBits(0) : filterBits; | ||
| // Indicates whether this result was produced via exact or approximate search. | ||
| private final SearchMode searchMode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cases when results will be acquired from exact search is the filters case right?
| // Forcing optimistic search for testing | ||
| systemProperty 'mem_opt_srch.force_reenter', 'true' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to run search for memory optimized cases then lets create another gradle task and also a new CI that runs that task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this to force it to run 2nd search in optimistic.
Since the 2nd search will kick off only if there's segment whose min score > the min score in merged results, it was tricky for me to make the data.
The base branch was changed.
ea03c31 to
21aa301
Compare
src/main/java/org/opensearch/knn/index/query/memoryoptsearch/MemoryOptimizedKNNWeight.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/knn/index/query/memoryoptsearch/MemoryOptimizedKNNWeight.java
Outdated
Show resolved
Hide resolved
23325ed to
0f44ba7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, some minor comments
src/main/java/org/opensearch/knn/index/query/memoryoptsearch/MemoryOptimizedKNNWeight.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
| final PerLeafResult perLeafResult = perLeafResults.get(i); | ||
| final TopDocs perLeaf = perLeafResults.get(i).getResult(); | ||
| if (perLeaf.scoreDocs.length > 0 && perLeafResult.getSearchMode() == PerLeafResult.SearchMode.APPROXIMATE_SEARCH) { | ||
| if (FORCE_REENTER_TESTING || perLeaf.scoreDocs[perLeaf.scoreDocs.length - 1].score >= minTopKScore) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Its not ideal to have a testing flag in final code, can't we create a test case where we mock scores?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since whether if it kicks off the second search will be entirely depending on data. Which is hard to be made, and this was the only solution I found so far to always enforce it to enter 2nd search in optimistic.
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Outdated
Show resolved
Hide resolved
0f44ba7 to
4410b34
Compare
|
@0ctopus13prime any reason why MacOS builds are failing? |
|
@navneet1v |
4410b34 to
0800030
Compare
…pensearch-project#2904) Signed-off-by: Dooyong Kim <[email protected]>
Signed-off-by: Dooyong Kim <[email protected]>
0800030 to
a43fa59
Compare
src/main/java/org/opensearch/knn/index/query/nativelib/NativeEngineKnnVectorQuery.java
Show resolved
Hide resolved
Signed-off-by: Dooyong Kim <[email protected]>
a43fa59 to
347347f
Compare
Description
RFC : #2924
Enable optimistic search for memory optimized search and deprecate MultiLeafKnnCollector which has an early termination logic.
This PR has three big changes:
Now, when memory-optimized search is enabled, all queries use
NativeEngineKnnVectorQuery.KnnQuery, which only provides aScorerSupplierand performs search within a single leaf segment (with the resultingScorerbeing consumed by an externalBulkScorerunder the standard Lucene search flow). But optimistic search requires coordination across segments. It needs to run an initial (first-phase) search, then identify and revisit only the segments likely to contain promising results.To support this coordinated two-phase process,
NativeEngineKnnVectorQueryis a more suitable entry point thanKnnQuery.Backported Lucene components required for optimistic search, specifically:
2.1. ReentrantKnnCollectorManager
2.2. SeededMappedDISI
2.3. SeededTopDocsDISI
Related Issues
Resolves #[Issue number to be closed when this PR is merged]
#2924
Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.