rajat315315 opened a new pull request, #16431:
URL: https://github.com/apache/lucene/pull/16431
Resolves: #16429
## Description
Currently, `FunctionScoreQuery` hardcodes `getMaxScore(int upTo)` to
`Float.POSITIVE_INFINITY` inside its `FilterScorer` implementation:
```java
// FunctionScoreQuery.java (Current Main)
@Override
public float getMaxScore(int upTo) throws IOException {
return Float.POSITIVE_INFINITY;
}
```
Because `getMaxScore()` returns `Float.POSITIVE_INFINITY`, **Block-Max WAND
(BMW) dynamic block pruning is disabled** for all function and script queries.
Lucene is forced to evaluate `DoubleValuesSource` and script bytecode on
**every single matching document** doc-by-doc, creating severe throughput
bottlenecks on large indices.
This PR enables native **Block-Max WAND dynamic block skipping for
`FunctionScoreQuery`** by leveraging the underlying segment's
`DocValuesSkipper` (as suggested by @jpountz).
---
## Proposed Changes
### 1. `DoubleValues.java` & `DoubleValuesSource.java` (`lucene/core`)
* Added `public int advanceShallow(int target)` and `public float
getMaxScore(int upTo)` to `DoubleValues` and `DoubleValuesSource`.
* Updated `FieldValuesSource` (`fromField`, `fromLongField`,
`fromDoubleField`, etc.) to query
`LeafReaderContext.reader().getDocValuesSkipper(field)`:
* `advanceShallow(target)` delegates to `skipper.advance(target)` to
advance shallow block boundaries.
* `getMaxScore(upTo)` returns `(float)
decoder.applyAsDouble(skipper.maxValue(0))` when block bounds are available.
### 2. `FunctionScoreQuery.java` (`lucene/queries`)
* Updated `FunctionScoreWeight.scorerSupplier()` so that
`FilterScorer.getMaxScore(upTo)` and `advanceShallow(target)` delegate directly
to `scores.getMaxScore(upTo)` and `scores.advanceShallow(target)` instead of
returning `Float.POSITIVE_INFINITY`:
```java
@Override
public int advanceShallow(int target) throws IOException {
int innerShallow = in.advanceShallow(target);
int scoreShallow = scores.advanceShallow(target);
return Math.min(innerShallow, scoreShallow);
}
@Override
public float getMaxScore(int upTo) throws IOException {
float innerMaxScore = in.getMaxScore(upTo);
float valueMaxScore = scores.getMaxScore(upTo);
if (Float.isInfinite(valueMaxScore)) {
return Float.POSITIVE_INFINITY;
}
return innerMaxScore * valueMaxScore * boost;
}
```
### 3. Unit Tests (`TestFunctionScoreQuery.java`)
Added unit test coverage in `TestFunctionScoreQuery.java`:
* `testMaxScoreDelegationWithDocValuesSkipper()`: Verifies that
`advanceShallow` and `getMaxScore` query `DocValuesSkipper` and return finite
score upper bounds.
* `testMaxScorePruningTopDocs()`: Verifies top-$K$ search correctness when
WAND dynamic block skipping is active.
---
## Benchmark Results (JMH)
We benchmarked this optimization using `ScriptWANDBlockCacheBenchmark` on a
**10 Million document index** ($N = 10,000,000$, $1,000,000$ matching documents
at $10\%$ match density, top-$100$ target hits) on OpenJDK 25:
| Block Size | Execution Strategy | Final Throughput | Speedup vs Baseline |
**Speedup vs 4096 Default** |
| :---: | :--- | :---: | :---: | :---: |
| **N/A** | **Baseline Unpruned Script Query** | **39.22 ops/sec** | **1.00x
(Baseline)** | **1.00x** |
| **4096 Docs** | **DocValues Skip Index WAND** | **43.63 ops/sec** | +11.2%
| **1.00x (Current Default)** |
| **1024 Docs** | **DocValues Skip Index WAND** | **77.30 ops/sec** | +97.1%
| **1.77x Faster** |
| **512 Docs** | **DocValues Skip Index WAND** | **118.41 ops/sec** |
+201.9% | **2.71x Faster** |
| **128 Docs** | **DocValues Skip Index WAND** | **204.73 ops/sec** | 🚀
**+422.0% (5.22x)** | 🚀 **4.69x FASTER than 4096** |
### Benchmark Insights:
1. **5.22x Speedup (+422.0% QPS)**: Enabling DocValues WAND dynamic block
pruning increases query throughput from **39.22 ops/sec to 204.73 ops/sec** on
10M documents by skipping non-competitive document blocks.
2. **Block Size Impact**: Testing varying block sizes confirms that smaller
DocValues skip block sizes (128 - 512) provide much finer WAND pruning
granularity than 4096-doc blocks, yielding **4.69x higher QPS**.
---
## Test Suite Verification
All **507 unit tests** in `lucene-queries` pass cleanly:
```bash
./gradlew :lucene:queries:test
# BUILD SUCCESSFUL: 507 tests, 5 skipped, 0 failures
```
cc @jpountz @romseygeek @mikemccand @uschindler
--
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: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]