Apply filter before scoring in MaxScoreBulkScorer - #16515
Conversation
| throws IOException { | ||
| do { | ||
| for (top.scorer.nextDocsAndScores(innerWindowMax, acceptDocs, docAndScoreBuffer); | ||
| for (nextDocsAndScores( |
There was a problem hiding this comment.
I wonder if there's a way of doing this that merges the filter into acceptDocs? That way we don't need to add new methods to TermScorer and we don't need to pass two parallel Objects around.
There was a problem hiding this comment.
Given that acceptDocs and filterMatches are never used simultaneously, it seems unnecessary to encapsulate them together.
There was a problem hiding this comment.
Creating a FilterBits also seems like a reasonable approach. If you're okay with it, I'll make the change accordingly.
There was a problem hiding this comment.
in fillScoreBufferViaBitSet(), this exists:
if (acceptDocs != null) {
acceptDocs.applyMask(filterMatches, innerWindowMin);
}But it never passes the acceptDocs to the collect:
collectEssentialScoresIntoWindow(top, innerWindowMax, innerWindowMin, null, filterMatches);So there is a problem if acceptDocs is null, in which point, there should be a way to have filterMatches be turned into its own (offset-acknowledging) Bits object. But other than that, the acceptDocs should be able to be used on its own. Though acceptDocs.applyMask(filterMatches, innerWindowMin) might be able to be optimized with a better implementation for this use case.
There was a problem hiding this comment.
Thanks, this is exactly the motivation for this change.
Whether acceptDocs is null or not, filterMatches is the mask used to filter the candidates in this path. When acceptDocs is present, we first merge live-doc filtering into filterMatches
Also, when acceptDocs is backed by FixedBits, applyMask(FixedBitSet, offset) uses the optimized word-level implementation, so this merge should already be efficient.
If you see another way to pass the combined mask efficiently, I would be very interested in exploring it.
|
@romseygeek Thank you very much for your review. I'm still doing benchmark, seems like perf drops when |
The int overflow caused the benchmark results to be highly misleading. @romseygeek Please help review in your spare time. In this Results:
The change improves throughput across all tested configurations, by 36% to 167%. As expected, the largest gains are with sparse filters, where more candidate documents can be discarded before score. Note: The changes from “Support two-phase filters in MaxScoreBulkScorer bitset path” are not included in the benchmark results. I will submit a separate PR for that change. |
| throws IOException { | ||
| do { | ||
| for (top.scorer.nextDocsAndScores(innerWindowMax, acceptDocs, docAndScoreBuffer); | ||
| for (nextDocsAndScores( |
There was a problem hiding this comment.
in fillScoreBufferViaBitSet(), this exists:
if (acceptDocs != null) {
acceptDocs.applyMask(filterMatches, innerWindowMin);
}But it never passes the acceptDocs to the collect:
collectEssentialScoresIntoWindow(top, innerWindowMax, innerWindowMin, null, filterMatches);So there is a problem if acceptDocs is null, in which point, there should be a way to have filterMatches be turned into its own (offset-acknowledging) Bits object. But other than that, the acceptDocs should be able to be used on its own. Though acceptDocs.applyMask(filterMatches, innerWindowMin) might be able to be optimized with a better implementation for this use case.
|
I I added a benchmark task set in After 19 benchmark rounds, the final results are: The more docs filterd, the better the returns. |
Description
Apple filter before scoring in MaxScoreBulkScorer. Resolves #16514