Skip to content

perf(fts): use block max for or tail bounds#7435

Open
BubbleCal wants to merge 2 commits into
mainfrom
yang/fts-or-tail-blockmax
Open

perf(fts): use block max for or tail bounds#7435
BubbleCal wants to merge 2 commits into
mainfrom
yang/fts-or-tail-blockmax

Conversation

@BubbleCal

@BubbleCal BubbleCal commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Performance Improvement

What is the performance issue or bottleneck?

FTS OR WAND kept lead postings that were moved back into tail using the posting list's full approximate upper bound. That made the tail bound too loose inside the active block-max window and reduced the pruning benefit available from existing per-block max-score metadata.

How does this PR improve performance?

When OR WAND moves a lead posting back into tail, it now uses the current block max as the tail upper bound if the active block window still covers the next target. If the window is expired or not applicable, it falls back to the posting list's approximate upper bound so later high-score blocks remain reachable.

The PR also advances a headless OR tail to the next compressed block window when needed, while avoiding no-progress advancement for plain postings.

This intentionally does not implement impacts skip / ImpactsDISI, shared-floor import, MaxScoreCache, or score-only WAND fast paths.

Benchmark context

Ablation comparison between main-style tail upper bounds and this OR tail block-max optimization:

Case Main-style tail bound QPS This optimization QPS Lift from this optimization
match_len3_k10 324.7 475.6 +46.5%
match_len3_k100 218.0 324.4 +48.8%
phrase_len3_stop1_k10 629.6 631.5 +0.3%
phrase_len3_stop1_k100 425.6 415.8 -2.3%

Validation

All Cargo commands were run with an isolated target directory.

  • cargo fmt --all
  • git diff --check
  • cargo test -p lance-index scalar::inverted::wand::tests::test_or_ -- --nocapture — 4 passed
  • cargo test -p lance-index scalar::inverted::wand::tests -- --nocapture — 34 passed
  • cargo check -p lance-index --tests
  • cargo check --workspace --tests --benches
  • cargo clippy --all --tests --benches -- -D warnings

@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer performance labels Jun 24, 2026
@BubbleCal BubbleCal marked this pull request as ready for review June 24, 2026 04:10
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.32432% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance-index/src/scalar/inverted/wand.rs 99.32% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

// A low-scoring tail can be the only iterator left in the current
// window. Move to the next window so a later high-scoring block is still
// reachable instead of ending the disjunction early.
self.update_max_scores(up_to + 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

When this path advances a headless tail window, update_max_scores() derives the next up_to from tail.peek() in the no-head/no-lead case. The tail heap is ordered by upper bound, so if the top tail is already on its final block while a lower-bound tail still has later compressed blocks, up_to becomes TERMINATED_DOC_ID and the OR path stops before visiting those later blocks, dropping valid matches.

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 performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants