Skip to content

perf(fts): add MAXSCORE for pure SHOULD queries - #8474

Merged
BubbleCal merged 1 commit into
mainfrom
yang/oss-1706-pure-should-maxscore
Aug 12, 2026
Merged

perf(fts): add MAXSCORE for pure SHOULD queries#8474
BubbleCal merged 1 commit into
mainfrom
yang/oss-1706-pure-should-maxscore

Conversation

@BubbleCal

@BubbleCal BubbleCal commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What is the performance issue?

Pure Boolean SHOULD queries currently use an eager sum disjunction. A dense low-scoring clause can keep shallow windows small even when it cannot make a document competitive, so the scorer repeatedly probes candidates that a clause-level MAXSCORE split could defer.

How does this PR improve performance?

  • Add conservative list-wide upper bounds for composable scorers.
  • Split eligible non-negative pure-SHOULD clauses into essential and non-essential sets.
  • Let only essential clauses drive candidates and shallow-window boundaries.
  • Probe non-essential clauses only when they can still make the current candidate competitive.
  • Preserve exact query-order f32 summation, inclusive score ties, Phrase two-phase confirmation, MUST_NOT ordering, and eager fallback for signed, grouped, unbounded, low-clause-count, or overflowed shapes.

Stack

This is 2/3 for OSS-1706:

  1. fix(fts): make score sum upper bounds conservative #8473 - conservative score-sum bounds
  2. perf(fts): add MAXSCORE for pure SHOULD queries #8474 - pure-SHOULD clause MAXSCORE
  3. feat(fts): expose pure SHOULD MAXSCORE metrics #8475 - metrics and end-to-end observability

Review this PR relative to branch yang/oss-1706-score-bounds.

Benchmark

Measured on this exact PR head with the deterministic sparse-high-score plus dense-low-score unit canary: 1,025 documents, 10 SHOULD clauses, and k=1. Lower is better.

Scenario / metric Baseline This PR Benefit
Candidate probes 8,201 probes 153 probes 53.60x fewer probes

Candidate probes count successful next/advance operations on instrumented clause scorers. It is an operational posting-candidate proxy, not a count of low-level doc-id comparisons inside PostingIterator. The 10M MMLB ABBA benchmark is reported on the exact full-stack head in #8475.

Validation

  • cargo fmt --all -- --check
  • cargo test -p lance-index scalar::inverted::compound::tests --lib -- --nocapture
  • cargo test -p lance-index compound_global_bound --lib
  • cargo test -p lance dataset::tests::dataset_index::test_pure_should_maxscore_is_exact_across_fragments --lib -- --exact --nocapture
  • cargo clippy --all --tests --benches -- -D warnings

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate recommendation: approve.

The change closes the compound clause-level thresholding gap with conservative list-wide bounds and a dedicated pure-SHOULD MAXSCORE driver. Keeping it specialized is preferable to changing every sum disjunction: it preserves exact query-order scoring, inclusive ties, phrase confirmation, MUST_NOT ordering, and safe eager fallback for unsupported bounds.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 11, 2026
Base automatically changed from yang/oss-1706-score-bounds to main August 11, 2026 14:31
@BubbleCal
BubbleCal force-pushed the yang/oss-1706-pure-should-maxscore branch from 5e4e9c3 to 86d7783 Compare August 11, 2026 14:31
@lance-gatekeeper lance-gatekeeper Bot removed the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 11, 2026

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate recommendation: approve.

The change closes the compound clause-level thresholding gap with conservative list-wide bounds and a dedicated pure-SHOULD MAXSCORE driver. Keeping it specialized is preferable to changing every sum disjunction: it preserves exact query-order scoring, inclusive ties, phrase confirmation, MUST_NOT ordering, and safe eager fallback for unsupported bounds.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 11, 2026
pull Bot pushed a commit to nagyist/lance that referenced this pull request Aug 11, 2026
## What is the bug?

Compound WAND score bounds sum non-negative clause maxima in f64 and
round upward once. Exact scoring accumulates f32 values recursively, and
a different clause order can produce a score one ULP above that bound. A
competitive document can therefore be pruned incorrectly.

## How does this PR fix it?

- Widen the f64 total with the existing clause-count upper-bound factor
before converting to f32.
- Round upward again when the f32 conversion rounds down.
- Add deterministic bit-level regressions for query-order and reordered
f32 accumulation.

## Stack

This is 1/3 for OSS-1706:

1. lance-format#8473 - conservative score-sum bounds
2. lance-format#8474 - pure-SHOULD clause MAXSCORE
3. lance-format#8475 - metrics and end-to-end observability

## Validation

- cargo fmt --all -- --check
- cargo test -p lance-index
scalar::inverted::wand::tests::conservative_score_sum_covers_query_order_f32_rounding
--lib -- --exact
- cargo clippy --all --tests --benches -- -D warnings
@BubbleCal
BubbleCal merged commit ddc2f37 into main Aug 12, 2026
40 checks passed
@BubbleCal
BubbleCal deleted the yang/oss-1706-pure-should-maxscore branch August 12, 2026 15:54
BubbleCal added a commit that referenced this pull request Aug 13, 2026
## What changed?

This final stack layer exposes the pure-SHOULD MAXSCORE work in FTS
execution metrics:

- compound_should_skipped_windows
- compound_should_bound_recomputations
- compound_should_essential_evaluations
- compound_should_non_essential_evaluations

The collector is threaded recursively so nested eligible Boolean scorers
report work. Counters are accumulated on the scorer hot path and flushed
independently when the scorer is dropped. The end-to-end test verifies
that production routing activates the optimization across four fragments
and reports non-zero bound recomputations and essential evaluations.

## Stack

This is 3/3 for OSS-1706 and is stacked on #8474, which is stacked on
#8473. Review this PR relative to branch
yang/oss-1706-pure-should-maxscore.

This PR head has the exact source tree used for the 10M MMLB benchmark
below.

## Benchmark

Environment and protocol: GCP c4-standard-16 VM; 10,000,000 MMLB rows in
10 fragments; one reused 37,785,665,554-byte FTS index; 8 query workers;
64 GiB cache; position streams prewarmed. Results use a two-block ABBA
run, four isolated process samples per build, 1,000 queries per case,
and geometric means. QPS is higher-is-better; p50 latency is
lower-is-better.

| Scenario / metric | Baseline | This PR | Benefit |
| --- | ---: | ---: | ---: |
| SHOULD x3, k=10, QPS | 159.77 queries/s | 465.76 queries/s | 2.92x
throughput |
| SHOULD x3, k=10, p50 | 31.02 ms | 9.21 ms | 3.37x speedup |
| SHOULD x3, k=100, QPS | 144.69 queries/s | 337.98 queries/s | 2.34x
throughput |
| SHOULD x3, k=100, p50 | 31.64 ms | 14.95 ms | 2.12x speedup |
| SHOULD + Phrase, k=10, QPS | 320.42 queries/s | 885.60 queries/s |
2.76x throughput |
| SHOULD + Phrase, k=10, p50 | 13.29 ms | 4.88 ms | 2.72x speedup |
| SHOULD + Phrase, k=100, QPS | 221.36 queries/s | 462.28 queries/s |
2.09x throughput |
| SHOULD + Phrase, k=100, p50 | 14.73 ms | 8.43 ms | 1.75x speedup |

MUST controls remained within 0.99x-1.01x. The reused index hash was
unchanged before and after the run. Exact-oracle checks passed 24/24 for
each build, all 56,000 timed result signatures matched across builds,
and there were no timed digest mismatches.

The existing index_comparisons metric is a WandCursor leaf-candidate
proxy rather than a low-level PostingIterator doc-id comparison count;
no stronger claim is made for that metric.

## Validation

- cargo fmt --all -- --check
- cargo test -p lance-index pure_should_maxscore --lib -- --nocapture
- cargo test -p lance
io::exec::fts::tests::test_compound_should_metrics_are_counted_independently
--lib -- --exact --nocapture
- cargo test -p lance
dataset::tests::dataset_index::test_pure_should_maxscore_is_exact_across_fragments
--lib -- --exact --nocapture
- cargo clippy --all --tests --benches -- -D warnings

Completes OSS-1706.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer K-approved Latest Gatekeeper recommendation permits acceptance. performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants