You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2026/04/27 12:58:26 UTC
(lucene) branch main updated: Optimize DisjunctionMaxBulkScorer: reuse inner collector and inline score reset (#15868)
This is an automated email from the ASF dual-hosted git repository.
jpountz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucene.git
The following commit(s) were added to refs/heads/main by this push:
new 5d135e741d9 Optimize DisjunctionMaxBulkScorer: reuse inner collector and inline score reset (#15868)
5d135e741d9 is described below
commit 5d135e741d95a070c60a15426f4fb2825d94f5ee
Author: Prithvi <pr...@gmail.com>
AuthorDate: Mon Apr 27 18:28:20 2026 +0530
Optimize DisjunctionMaxBulkScorer: reuse inner collector and inline score reset (#15868)
---
lucene/CHANGES.txt | 3 ++
.../lucene/search/DisjunctionMaxBulkScorer.java | 63 +++++++++++-----------
2 files changed, 35 insertions(+), 31 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index a94ecc0bf15..2714afd31c5 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -129,6 +129,9 @@ Improvements
Optimizations
---------------------
+* GITHUB#15868: Optimize DisjunctionMaxBulkScorer by reusing the inner LeafCollector across
+ sub-scorers and resetting windowScores inline during replay instead of Arrays.fill. (Prithvi S)
+
* GITHUB#15681, GITHUB#15833: Replace pre-sized array or empty array with lambda expression to call Collection#toArray. (Zhou Hui)
* GITHUB#13782: Replace handwritten loops compare with Arrays.compareUnsigned in TermsEnum and TermsEnumFrame classes. (Zhou Hui)
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxBulkScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxBulkScorer.java
index 25dd6ab7f3a..83e54489d87 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxBulkScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxBulkScorer.java
@@ -17,7 +17,6 @@
package org.apache.lucene.search;
import java.io.IOException;
-import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -46,6 +45,32 @@ final class DisjunctionMaxBulkScorer extends BulkScorer {
private final float[] windowScores = new float[WINDOW_SIZE];
private final PriorityQueue<BulkScorerAndNext> scorers;
private final SimpleScorable topLevelScorable = new SimpleScorable();
+ private int windowMin;
+
+ /**
+ * Reusable inner {@link LeafCollector} that collects matches into the window bitset and scores
+ * array. This avoids allocating a new anonymous collector object per sub-scorer per window.
+ */
+ private final LeafCollector innerCollector =
+ new LeafCollector() {
+
+ private Scorable scorer;
+
+ @Override
+ public void setScorer(Scorable scorer) throws IOException {
+ this.scorer = scorer;
+ if (topLevelScorable.minCompetitiveScore != 0f) {
+ scorer.setMinCompetitiveScore(topLevelScorable.minCompetitiveScore);
+ }
+ }
+
+ @Override
+ public void collect(int doc) throws IOException {
+ final int delta = doc - windowMin;
+ windowMatches.set(delta);
+ windowScores[delta] = Math.max(windowScores[delta], scorer.score());
+ }
+ };
DisjunctionMaxBulkScorer(List<BulkScorer> scorers) {
if (scorers.size() < 2) {
@@ -61,51 +86,27 @@ final class DisjunctionMaxBulkScorer extends BulkScorer {
BulkScorerAndNext top = scorers.top();
while (top.next < max) {
- final int windowMin = Math.max(top.next, min);
+ windowMin = Math.max(top.next, min);
final int windowMax = MathUtil.unsignedMin(max, windowMin + WINDOW_SIZE);
// First compute matches / scores in the window
do {
- top.next =
- top.scorer.score(
- new LeafCollector() {
-
- private Scorable scorer;
-
- @Override
- public void setScorer(Scorable scorer) throws IOException {
- this.scorer = scorer;
- if (topLevelScorable.minCompetitiveScore != 0f) {
- scorer.setMinCompetitiveScore(topLevelScorable.minCompetitiveScore);
- }
- }
-
- @Override
- public void collect(int doc) throws IOException {
- final int delta = doc - windowMin;
- windowMatches.set(delta);
- windowScores[delta] = Math.max(windowScores[delta], scorer.score());
- }
- },
- acceptDocs,
- windowMin,
- windowMax);
+ top.next = top.scorer.score(innerCollector, acceptDocs, windowMin, windowMax);
top = scorers.updateTop();
} while (top.next < windowMax);
- // Then replay
+ // Then replay, resetting windowScores entries inline to avoid a full Arrays.fill
collector.setScorer(topLevelScorable);
for (int windowDoc = windowMatches.nextSetBit(0);
windowDoc != DocIdSetIterator.NO_MORE_DOCS;
windowDoc = windowMatches.nextSetBit(windowDoc + 1)) {
- int doc = windowMin + windowDoc;
topLevelScorable.score = windowScores[windowDoc];
- collector.collect(doc);
+ windowScores[windowDoc] = 0f;
+ collector.collect(windowMin + windowDoc);
}
- // Finally clean up state
+ // Only the bitset needs clearing; windowScores entries were reset during replay above.
windowMatches.clear();
- Arrays.fill(windowScores, 0f);
}
return top.next;