You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@lucene.apache.org by "costin (via GitHub)" <gi...@apache.org> on 2026/05/27 21:21:04 UTC
[PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
costin opened a new pull request, #16141:
URL: https://github.com/apache/lucene/pull/16141
`DefaultBulkScorer` iterates doc-by-doc calling `collector.collect(int)` for each match. When the scorer is a `ConstantScoreScorer` with no two-phase iterator and no competitive iterator, we can instead batch doc IDs into a `FixedBitSet` window via `intoBitSet`, apply the live-docs mask in bulk, and hand the result as a `DocIdStream` to the collector.
This replaces per-doc virtual dispatch with bulk bitwise operations over a 4096-doc window. The new path triggers for `ConstantScoreQuery` filters, constant-score multi-term queries (`WildcardQuery`, `PrefixQuery`, `RegexpQuery`), and filter clauses in boolean queries.
### Benchmarks
`DefaultBulkScorerBenchmark` (1M docs, varying match density, ±10% deletions)
```
JDK 25.0.3+9-LTS (Temurin), JMH 1.37
JVM args: -Xmx2g -Xms2g -XX:+AlwaysPreTouch
Warmup: 3 x 5s, Measurement: 5 x 5s, Fork: 2
Mode: Throughput (ops/ms, higher is better)
```
**AMD EPYC 7R32 (c5a.2xlarge)**
| density | deletions | baseline | intoBitSet | speedup |
|---------|-----------|----------|------------|---------|
| 1% | no | 25.2 | 43.4 | 1.7x |
| 1% | yes | 16.7 | 27.9 | 1.7x |
| 10% | no | 2.6 | 43.2 | 17x |
| 10% | yes | 1.7 | 28.2 | 17x |
| 50% | no | 0.5 | 61.7 | 115x |
| 100% | no | 0.2 | 61.9 | 358x |
**Intel Xeon 8275CL (c5.2xlarge)**
| density | deletions | baseline | intoBitSet | speedup |
|---------|-----------|----------|------------|---------|
| 1% | no | 25.2 | 41.2 | 1.6x |
| 1% | yes | 20.2 | 25.9 | 1.3x |
| 10% | no | 2.5 | 41.2 | 16x |
| 10% | yes | 2.2 | 25.9 | 12x |
| 50% | no | 0.5 | 57.9 | 106x |
| 100% | no | 0.2 | 58.0 | 259x |
The baseline degrades linearly with density (one virtual call per matching doc). The intoBitSet path stays flat (cost scales with window size, not match count). Real filter queries typically match 1-10% of docs where the gain is 1.3-17x. The 100x+ ratios at high density apply to broad filters (`exists`, match-all, wide ranges).
This microbenchmark isolates scoring+collection with a counting collector. End-to-end query improvement will be smaller since collection is one component alongside posting decoding and result materialization.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3315513146
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java:
##########
@@ -147,6 +147,10 @@ public float score() throws IOException {
return score;
}
+ boolean canBulkCollectDocIdStream() {
+ return scoreMode != ScoreMode.TOP_SCORES;
+ }
Review Comment:
Thanks, the `AssertingScorer` comment pointed at the real issue. The fast path was gated only on `scoreMode != TOP_SCORES`, but `ScoreMode.COMPLETE` can still have collectors that call `scorer.score()` while replaying docs from the `DocIdStream`. Since `intoBitSet()` advances the iterator before replay, that violates the scorer positioning contract and trips `AssertingScorer`.
I updated the gate to only use `DocIdStream` batching when `scoreMode.needsScores() == false`, and added a regression test for `ScoreMode.COMPLETE` staying on the per-doc path.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "romseygeek (via GitHub)" <gi...@apache.org>.
romseygeek commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3316474928
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +281,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
Review Comment:
Instanceof checks feel a bit off to me here. I wonder if we should look at implementing a ConstantScoreBulkScorer?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "romseygeek (via GitHub)" <gi...@apache.org>.
romseygeek merged PR #16141:
URL: https://github.com/apache/lucene/pull/16141
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3315513065
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -228,9 +230,12 @@ public long cost() {
* @lucene.internal
*/
protected static class DefaultBulkScorer extends BulkScorer {
+ private static final int WINDOW_SIZE = 1 << 12;
Review Comment:
Done, switched DefaultBulkScorer to reuse DenseConjunctionBulkScorer.WINDOW_SIZE.
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java:
##########
@@ -147,6 +147,10 @@ public float score() throws IOException {
return score;
}
+ boolean canBulkCollectDocIdStream() {
+ return scoreMode != ScoreMode.TOP_SCORES;
+ }
Review Comment:
No AssertingScorer change needed: the current targeted tests pass, and the optimized path remains guarded to non-TOP_SCORES ConstantScoreScorer.
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -300,6 +310,27 @@ private static void scoreIterator(
}
}
+ private void scoreIteratorIntoBitSet(
+ LeafCollector collector, Bits acceptDocs, DocIdSetIterator iterator, int max)
+ throws IOException {
+ for (int doc = iterator.docID(); doc < max; ) {
+ int windowBase = doc;
+ int windowMax = MathUtil.unsignedMin(max, windowBase + WINDOW_SIZE);
+
+ assert windowMatches.scanIsEmpty();
+ iterator.intoBitSet(windowMax, windowMatches, windowBase);
+
+ if (acceptDocs != null) {
Review Comment:
Done, the bitset path now skips both live-doc masking and DocIdStream collection when the window is empty.
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +282,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
+ && constantScoreScorer.canBulkCollectDocIdStream()) {
+ scoreIteratorIntoBitSet(collector, acceptDocs, iterator, max);
Review Comment:
I left out the density gate intentionally: the benchmark still shows gains at 1% density, and DenseConjunctionBulkScorer’s threshold is for multi-clause bitset intersections. The new empty-window skip handles sparse gaps without disabling the measured sparse-case win.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3317179405
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +281,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
Review Comment:
Incorporated `ConstantScoreBulkScorer` in this PR.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "romseygeek (via GitHub)" <gi...@apache.org>.
romseygeek commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3317237142
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreBulkScorer.java:
##########
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.MathUtil;
+
+/**
+ * Bulk scorer for no-score constant-score iterators that batches doc IDs via {@link
+ * DocIdSetIterator#intoBitSet}.
+ */
+final class ConstantScoreBulkScorer extends BulkScorer {
+ private final Scorer scorer;
+ private final DocIdSetIterator iterator;
+ private final FixedBitSet windowMatches = new FixedBitSet(DenseConjunctionBulkScorer.WINDOW_SIZE);
+
+ ConstantScoreBulkScorer(float score, ScoreMode scoreMode, DocIdSetIterator iterator) {
+ if (scoreMode.needsScores()) {
+ throw new IllegalArgumentException("ScoreMode must not need scores: " + scoreMode);
+ }
+ if (TwoPhaseIterator.unwrap(iterator) != null) {
+ throw new IllegalArgumentException("Iterator must not wrap a TwoPhaseIterator");
+ }
+ this.scorer = new ConstantScoreScorer(score, scoreMode, iterator);
+ this.iterator = iterator;
+ }
+
+ @Override
+ public int score(LeafCollector collector, Bits acceptDocs, int min, int max) throws IOException {
+ collector.setScorer(scorer);
+ DocIdSetIterator competitiveIterator = collector.competitiveIterator();
+ if (competitiveIterator != null) {
+ scoreIterator(collector, acceptDocs, iterator, competitiveIterator, min, max);
+ } else {
+ scoreIteratorIntoBitSet(collector, acceptDocs, iterator, min, max);
+ }
+ return iterator.docID();
+ }
+
+ @Override
+ public long cost() {
+ return iterator.cost();
+ }
+
+ private static void scoreIterator(
+ LeafCollector collector,
+ Bits acceptDocs,
+ DocIdSetIterator iterator,
+ DocIdSetIterator competitiveIterator,
+ int min,
+ int max)
+ throws IOException {
+ if (competitiveIterator.docID() > min) {
+ min = Math.min(competitiveIterator.docID(), max);
+ }
+ if (iterator.docID() < min) {
+ if (iterator.docID() == min - 1) {
+ iterator.nextDoc();
+ } else {
+ iterator.advance(min);
+ }
+ }
+ for (int doc = iterator.docID(); doc < max; ) {
+ assert competitiveIterator.docID() <= doc; // invariant
+ if (competitiveIterator.docID() < doc) {
+ int competitiveNext = competitiveIterator.advance(doc);
+ if (competitiveNext != doc) {
+ doc = iterator.advance(competitiveNext);
+ continue;
+ }
+ }
+
+ if (acceptDocs == null || acceptDocs.get(doc)) {
+ collector.collect(doc);
+ }
+
+ doc = iterator.nextDoc();
+ }
+ }
+
+ private void scoreIteratorIntoBitSet(
+ LeafCollector collector, Bits acceptDocs, DocIdSetIterator iterator, int min, int max)
+ throws IOException {
+ if (iterator.docID() < min) {
+ if (iterator.docID() == min - 1) {
+ iterator.nextDoc();
+ } else {
+ iterator.advance(min);
+ }
+ }
+ for (int doc = iterator.docID(); doc < max; ) {
+ int windowBase = doc;
+ int windowMax =
+ MathUtil.unsignedMin(max, windowBase + DenseConjunctionBulkScorer.WINDOW_SIZE);
+
+ assert windowMatches.scanIsEmpty();
+ iterator.intoBitSet(windowMax, windowMatches, windowBase);
+
+ if (windowMatches.scanIsEmpty() == false && acceptDocs != null) {
+ acceptDocs.applyMask(windowMatches, windowBase);
+ }
+
+ if (windowMatches.scanIsEmpty() == false) {
Review Comment:
Same here
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreBulkScorer.java:
##########
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.MathUtil;
+
+/**
+ * Bulk scorer for no-score constant-score iterators that batches doc IDs via {@link
+ * DocIdSetIterator#intoBitSet}.
+ */
+final class ConstantScoreBulkScorer extends BulkScorer {
+ private final Scorer scorer;
+ private final DocIdSetIterator iterator;
+ private final FixedBitSet windowMatches = new FixedBitSet(DenseConjunctionBulkScorer.WINDOW_SIZE);
+
+ ConstantScoreBulkScorer(float score, ScoreMode scoreMode, DocIdSetIterator iterator) {
+ if (scoreMode.needsScores()) {
+ throw new IllegalArgumentException("ScoreMode must not need scores: " + scoreMode);
+ }
+ if (TwoPhaseIterator.unwrap(iterator) != null) {
+ throw new IllegalArgumentException("Iterator must not wrap a TwoPhaseIterator");
+ }
+ this.scorer = new ConstantScoreScorer(score, scoreMode, iterator);
+ this.iterator = iterator;
+ }
+
+ @Override
+ public int score(LeafCollector collector, Bits acceptDocs, int min, int max) throws IOException {
+ collector.setScorer(scorer);
+ DocIdSetIterator competitiveIterator = collector.competitiveIterator();
+ if (competitiveIterator != null) {
+ scoreIterator(collector, acceptDocs, iterator, competitiveIterator, min, max);
+ } else {
+ scoreIteratorIntoBitSet(collector, acceptDocs, iterator, min, max);
+ }
+ return iterator.docID();
+ }
+
+ @Override
+ public long cost() {
+ return iterator.cost();
+ }
+
+ private static void scoreIterator(
+ LeafCollector collector,
+ Bits acceptDocs,
+ DocIdSetIterator iterator,
+ DocIdSetIterator competitiveIterator,
+ int min,
+ int max)
+ throws IOException {
+ if (competitiveIterator.docID() > min) {
+ min = Math.min(competitiveIterator.docID(), max);
+ }
+ if (iterator.docID() < min) {
+ if (iterator.docID() == min - 1) {
+ iterator.nextDoc();
+ } else {
+ iterator.advance(min);
+ }
+ }
+ for (int doc = iterator.docID(); doc < max; ) {
+ assert competitiveIterator.docID() <= doc; // invariant
+ if (competitiveIterator.docID() < doc) {
+ int competitiveNext = competitiveIterator.advance(doc);
+ if (competitiveNext != doc) {
+ doc = iterator.advance(competitiveNext);
+ continue;
+ }
+ }
+
+ if (acceptDocs == null || acceptDocs.get(doc)) {
+ collector.collect(doc);
+ }
+
+ doc = iterator.nextDoc();
+ }
+ }
+
+ private void scoreIteratorIntoBitSet(
+ LeafCollector collector, Bits acceptDocs, DocIdSetIterator iterator, int min, int max)
+ throws IOException {
+ if (iterator.docID() < min) {
+ if (iterator.docID() == min - 1) {
+ iterator.nextDoc();
+ } else {
+ iterator.advance(min);
+ }
+ }
+ for (int doc = iterator.docID(); doc < max; ) {
+ int windowBase = doc;
+ int windowMax =
+ MathUtil.unsignedMin(max, windowBase + DenseConjunctionBulkScorer.WINDOW_SIZE);
+
+ assert windowMatches.scanIsEmpty();
+ iterator.intoBitSet(windowMax, windowMatches, windowBase);
+
+ if (windowMatches.scanIsEmpty() == false && acceptDocs != null) {
Review Comment:
`scanIsEmpty()` is only used in assertions elsewhere, and it can be expensive so let's avoid it here?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3315771868
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +282,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
+ && constantScoreScorer.canBulkCollectDocIdStream()) {
+ scoreIteratorIntoBitSet(collector, acceptDocs, iterator, max);
Review Comment:
Ran the benchmark at the sparse densities mentioned (Apple M3/aarch64). Results (1M docs, no deletions):
```
Density Docs Baseline (ops/ms) intoBitSet (ops/ms) Winner
0.01% 100 387 169 Baseline 2.3x
0.1% 1,000 181 105 Baseline 1.7x
0.2% 2,000 108 89 Baseline 1.2x
0.3% 3,000 62 86 intoBitSet 1.4x ← crossover
0.5% 5,000 46 86 intoBitSet 1.9x
1% 10,000 47 85 intoBitSet 1.8x
3% 30,000 8 84 intoBitSet 10.9x
```
With deletions the crossover shifts slightly (between 0.1% and 0.5%) but the pattern is the same.
The crossover is around 0.25% density. Below that, per-window overhead (clearing + iterating ~244 windows for 1M docs) exceeds the per-doc savings. Above that, intoBitSet wins and the gap widens fast.
In absolute terms the regression at extreme sparsity is small. At 0.01% density (100 matching docs in 1M), the difference is ~3.3µs per query. At 10M docs, ~33µs. Both are insignificant compared to the milliseconds of a typical Lucene query or even a single disk seek (50-100µs).
Queries that land on `DefaultBulkScorer` at <0.3% density are also rare in practice since filters that selective typically resolve through more specialized scorers.
That said, I can add the density gate if you prefer. Something like `cost >= (max - min) / 512` would match the observed crossover without being overly conservative.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3316657024
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +281,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
Review Comment:
I agree; I can follow up with a small refactor PR if that works (as I expect `ConstantScoreBulkScorer` might be useful in other places).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "jainankitk (via GitHub)" <gi...@apache.org>.
jainankitk commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3314427877
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -228,9 +230,12 @@ public long cost() {
* @lucene.internal
*/
protected static class DefaultBulkScorer extends BulkScorer {
+ private static final int WINDOW_SIZE = 1 << 12;
Review Comment:
I am assuming this value of `WINDOW_SIZE` is coming from `DenseConjunctionBulkScorer`. Should we use common value at both the places?
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java:
##########
@@ -147,6 +147,10 @@ public float score() throws IOException {
return score;
}
+ boolean canBulkCollectDocIdStream() {
+ return scoreMode != ScoreMode.TOP_SCORES;
+ }
Review Comment:
Failing tests might need change in `AssertingScorer`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "romseygeek (via GitHub)" <gi...@apache.org>.
romseygeek commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3316482339
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java:
##########
@@ -147,6 +147,10 @@ public float score() throws IOException {
return score;
}
+ boolean canBulkCollectDocIdStream() {
+ return scoreMode != ScoreMode.TOP_SCORES;
+ }
Review Comment:
Yeah I think we need to tighten the API around collectRange() and collect(DocIdStream) - it doesn't make sense to get scores inside these methods because the Scorer isn't going to be correctly positioned.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "jainankitk (via GitHub)" <gi...@apache.org>.
jainankitk commented on PR #16141:
URL: https://github.com/apache/lucene/pull/16141#issuecomment-4585977200
Updated milestone to `10.5` as this was backported into `branch_10x`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3316674785
##########
lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java:
##########
@@ -147,6 +147,10 @@ public float score() throws IOException {
return score;
}
+ boolean canBulkCollectDocIdStream() {
+ return scoreMode != ScoreMode.TOP_SCORES;
+ }
Review Comment:
I can take this as a follow-up as it touches broader collector/scorer API semantics.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on PR #16141:
URL: https://github.com/apache/lucene/pull/16141#issuecomment-4563904223
I reran `DefaultBulkScorerBenchmark` locally on the latest PR branch on my laptop (so there's some noise) after removing the runtime `scanIsEmpty()` calls. While the raw numbers are different than on the original post, the relative speedup remains.
Command:
`java -jar lucene/benchmark-jmh/build/benchmarks/lucene-benchmark-jmh-11.0.0-SNAPSHOT.jar org.apache.lucene.benchmark.jmh.DefaultBulkScorerBenchmark -rf json -rff /tmp/default-bulkscorer-benchmark.json`
JDK: Temurin 25.0.3. Higher throughput is better.
| density | deletions | baseline ops/ms | intoBitSet ops/ms | speedup |
|---:|:---:|---:|---:|---:|
| 0.01 | false | 47.112 | 83.921 | 1.8x |
| 0.01 | true | 29.248 | 53.997 | 1.8x |
| 0.1 | false | 3.081 | 66.020 | 21.4x |
| 0.1 | true | 2.472 | 45.431 | 18.4x |
| 0.5 | false | 0.537 | 84.557 | 157.6x |
| 0.5 | true | 0.444 | 60.437 | 136.2x |
| 1.0 | false | 0.232 | 404.220 | 1745.1x |
| 1.0 | true | 0.227 | 3.091 | 13.6x |
|
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "sgup432 (via GitHub)" <gi...@apache.org>.
sgup432 commented on code in PR #16141:
URL: https://github.com/apache/lucene/pull/16141#discussion_r3314753945
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -300,6 +310,27 @@ private static void scoreIterator(
}
}
+ private void scoreIteratorIntoBitSet(
+ LeafCollector collector, Bits acceptDocs, DocIdSetIterator iterator, int max)
+ throws IOException {
+ for (int doc = iterator.docID(); doc < max; ) {
+ int windowBase = doc;
+ int windowMax = MathUtil.unsignedMin(max, windowBase + WINDOW_SIZE);
+
+ assert windowMatches.scanIsEmpty();
+ iterator.intoBitSet(windowMax, windowMatches, windowBase);
+
+ if (acceptDocs != null) {
Review Comment:
Should we short circuit here if windowMatches is empty after intoBitSet? We could skip both applyMask and collector.collect in that case
##########
lucene/core/src/java/org/apache/lucene/search/Weight.java:
##########
@@ -277,7 +282,12 @@ public int score(LeafCollector collector, Bits acceptDocs, int min, int max)
// iterator implementations.
if (twoPhase == null && competitiveIterator == null) {
// Optimize simple iterators with collectors that can't skip
- scoreIterator(collector, acceptDocs, iterator, max);
+ if (scorer instanceof ConstantScoreScorer constantScoreScorer
+ && constantScoreScorer.canBulkCollectDocIdStream()) {
+ scoreIteratorIntoBitSet(collector, acceptDocs, iterator, max);
Review Comment:
I wonder if we should this for sparse use-case? As the batching overhead might exceed per-doc collection when windows are mostly empty.
So we can add some density checks like:
```
if (iterator.cost() >= range / 32) {
// call scoreIteratorIntoBitSet
} else {
// otherwise fallback to per doc way
}
```
Something similar happens in DenseConjunctionBulkScorer.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "romseygeek (via GitHub)" <gi...@apache.org>.
romseygeek commented on PR #16141:
URL: https://github.com/apache/lucene/pull/16141#issuecomment-4563558695
Looks great, thanks @costin! What do the benchmark results look like now?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org
Re: [PR] Optimize DefaultBulkScorer for ConstantScoreScorer via intoBitSet batching [lucene]
Posted by "costin (via GitHub)" <gi...@apache.org>.
costin commented on PR #16141:
URL: https://github.com/apache/lucene/pull/16141#issuecomment-4563461029
Addressed the comments, @romseygeek please take another look. Thanks!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org