Skip to content

perf(vindex): execute compatible queries through native batch search - #646

Merged
JingsongLi merged 3 commits into
apache:mainfrom
JunRuiLee:perf/vindex-native-batch
Aug 4, 2026
Merged

perf(vindex): execute compatible queries through native batch search#646
JingsongLi merged 3 commits into
apache:mainfrom
JunRuiLee:perf/vindex-native-batch

Conversation

@JunRuiLee

@JunRuiLee JunRuiLee commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Purpose

This is PR 2/3 of a stacked vector-search performance series and builds on #645.

PR #645 introduced positional range reads while deliberately keeping multi-query
global-index vindex searches on the whole-file buffered path. This PR replaces
that fallback with the native batch-search APIs from paimon-vindex-core, while
retaining positional range reads.

Brief change log

  • Prepare and validate each query before backend execution.
  • Group queries with compatible top_k, nprobe, and row-id filters.
  • Execute each compatible group in a single native vindex batch-search call.
  • Rely on vindex-core's bounded posting-list read batches and oversized-list
    streaming instead of imposing a Rust-side query-count cap.
  • Retain scalar execution for groups containing a single query.
  • Restore results to their original query order.
  • Validate backend result dimensions before slicing per-query results.
  • Reuse probed posting lists across compatible queries instead of repeating
    scalar range reads.

PK-vector ANN segments remain on their existing buffered path until #647. The
reader validation and optimization hooks introduced here intentionally prepare
the lifecycle consumed by #647.

Tests

  • cargo test -p paimon vindex --lib — 123 passed
  • cargo test -p paimon vector_search_builder --lib — 77 passed
  • cargo test -p paimon --lib — 2096 passed, 1 ignored
  • cargo fmt --check
  • git diff --check

Coverage verifies mixed-group ordering, scalar equivalence, filtered native
batch execution, range reuse, avoidance of full-file reads, reuse across the
former 16-query boundary, and graceful failure outside a Tokio runtime.

API and Format

No public API or storage-format changes. Existing batch result ordering is
preserved.

Documentation

No user-facing documentation changes are required.

@JunRuiLee
JunRuiLee force-pushed the perf/vindex-native-batch branch from f2403ec to 70fb052 Compare August 1, 2026 06:20
@JunRuiLee
JunRuiLee force-pushed the perf/vindex-native-batch branch 6 times, most recently from 5037330 to 7769355 Compare August 3, 2026 11:33
@JunRuiLee
JunRuiLee marked this pull request as ready for review August 3, 2026 11:33
@JunRuiLee
JunRuiLee marked this pull request as draft August 3, 2026 12:00
@JunRuiLee
JunRuiLee force-pushed the perf/vindex-native-batch branch from 7769355 to 414853f Compare August 3, 2026 12:01
@JunRuiLee
JunRuiLee marked this pull request as ready for review August 3, 2026 12:03

@JingsongLi JingsongLi 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.

I found three issues in the new native-batch and range-read path.

})?;
let source = VindexFileReader::new(
Arc::new(file_reader),
current_tokio_runtime_handle()?,

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.

[P2] Preserve the non-Tokio batch fallback

This now unconditionally requires a Tokio handle, but before this change the vector_searches.len() > 1 branch read the file into a Cursor and therefore worked when polled by async-std, smol, or a plain futures executor. A public batch with two or more queries will now fail before searching with Vector index range reader requires a Tokio runtime.

Please retain the whole-file Cursor fallback when Handle::try_current() fails, or make the range bridge executor-agnostic, and add a non-Tokio batch regression test. The existing test only covers the pre-existing single-query limitation.

Comment thread crates/paimon/src/vindex/reader.rs Outdated
source: Some(Box::new(e)),
})?,
None => reader
.search_batch(&queries, indices.len(), params)

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.

[P2] Bound the native batch by its full working set

Every compatible group is flattened and passed to one native batch call. In paimon-vindex-core 0.3.0, this allocates a query_count * nlist f32 centroid-product matrix, plus query copies, per-query heaps, and query_count * top_k result arrays; L2 IVF-PQ also retains per-query m * ksub tables. Posting-list reads are byte-bounded, but this query-side scratch is not.

For example, 10,000 queries with nlist=4096 consume about 164 MB per active index-file job for the centroid matrix alone. With the default 32 concurrent shard jobs, those matrices alone can exceed 5 GB. The previous scalar loop bounded this scratch to one query at a time. Please chunk compatible groups using a working-set budget derived from at least nlist, dimension, top_k, and shard concurrency, then restore results to their original positions.

source: None,
}
})?;
let source = VindexFileReader::new(

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.

[P2] Apply global-index.thread-num to aggregate range I/O

Each admitted shard now creates an independent VindexFileReader, and each reader owns a private 32-permit range-read semaphore. The outer scheduler already admits up to global-index.thread-num shard jobs (default 32), so native multi-range reads can drive up to 32 * 32 = 1024 concurrent FileRead::read calls. This defeats the option's documented per-operation global-index I/O fan-out limit; in particular, setting it to 1 no longer reproduces strictly sequential I/O. The removed batch path issued one whole-file read per admitted shard.

Please share one operation-wide semaphore across all readers, or otherwise divide the range-read budget across admitted shard jobs.

@JingsongLi JingsongLi 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.

+1

@JingsongLi
JingsongLi merged commit cec2d4c into apache:main Aug 4, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants