Skip to content

perf(index): parallelize kmeans centroid recomputation - #8560

Open
isaac-dasari wants to merge 7 commits into
lance-format:mainfrom
isaac-dasari:perf/kmeans-centroid-recompute-6369
Open

perf(index): parallelize kmeans centroid recomputation#8560
isaac-dasari wants to merge 7 commits into
lance-format:mainfrom
isaac-dasari:perf/kmeans-centroid-recompute-6369

Conversation

@isaac-dasari

Copy link
Copy Markdown

Summary

  • recompute KMeans centroids by partitioning input vectors across workers so each input vector is scanned once
  • merge thread-local centroid accumulators in parallel and cap their aggregate allocation at 64 MiB
  • add correctness coverage for f16, f32, f64, ignored memberships, empty-cluster splitting, and the memory cap
  • add a focused Criterion benchmark and include it in the existing Rust benchmark workflow

The previous implementation divided centroids among workers, but every worker scanned the complete input. That made centroid recomputation perform O(num_vectors * workers) input reads. This changes the input scan to O(num_vectors) while avoiding shared-write contention.

Closes #6369.

Validation

Passed locally:

  • cargo fmt --all -- --check
  • cargo check -p lance-index --tests --benches --profile bench -j 1
  • cargo clippy -p lance-index --tests --benches --profile bench -j 1 -- -D warnings
  • git diff --check

The optimized test/benchmark linker exceeded the available resources on the local 7.6 GiB development machine, so executable test and benchmark runs are intentionally delegated to GitHub Actions. The PR is draft pending those results.

@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer A-ci CI / build workflows performance labels Aug 16, 2026
@isaac-dasari
isaac-dasari marked this pull request as ready for review August 17, 2026 07:00
lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Aug 17, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Aug 17, 2026
lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Aug 17, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Aug 20, 2026
lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 20, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 22, 2026
lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Aug 22, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Aug 23, 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: request changes.

The revision fixes the 512-row fallback and restores owner-local row order, but its row-count-only selector still sends every larger committed workload through an indexed path that remains slower than the live-base rescan.

Keep the owner-rescan path unless selection accounts for worker count, k, dimension, and measured index amortization; none of the committed indexed shapes demonstrates a win.

Comment thread rust/lance-index/src/vector/kmeans.rs Outdated
// short membership rescans it replaces. Keep the existing owner-rescan
// path there and reserve indexing for workloads large enough to amortize
// its two linear setup passes.
if num_vectors < MIN_OWNER_INDEX_ROWS {

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.

This row-count-only cutoff still selects owner indexing when its two serial construction passes, allocation, and indirect row lookup cost more than the membership rescans they replace. Against live base dd08336c, identical 62-thread release-with-debug batched harnesses measured all four committed shapes that take the index path slower on 90894283:

  • default_ivf_high_dim: 0.329 → 0.605 ms median (+83.9%).
  • default_pq_subvector: 0.327 → 0.743 ms (+127.2%).
  • max_sample_low_dim: 0.668 → 1.269 ms (+90.0%).
  • large_incremental_ivf: 2.956 → 3.212 ms (+8.7%).

The 512-row fallback reaches parity (0.271 → 0.269 ms), confirming that the rescan path fixes the case where it is selected. Two-thread runs also measured PQ 0.744 → 0.946 ms (+27.2%) and IVF 2.089 → 2.169 ms (+3.8%), so worker count must be part of the decision. This is the current successor to the earlier performance finding: owner grouping fixes cluster-major locality, but the selector still needs to retain owner rescans unless the complete workload cost predicts an indexed-path win.

Reproducer

Both exact-head harnesses used the five committed shapes, cyclic membership, allocation outside timing, two warmups, 20 samples, and 50 calls per sample. These commands produced the 62-thread results:

LANCE_CPU_THREADS=62 RAYON_NUM_THREADS=62 BENCH_SAMPLES=20 BENCH_WARMUPS=2 BENCH_BATCH=50 /home/agent/tmp/pr8560-dd0-target-bench/release-with-debug/pr8560-kmeans-bench
LANCE_CPU_THREADS=62 RAYON_NUM_THREADS=62 BENCH_SAMPLES=20 BENCH_WARMUPS=2 BENCH_BATCH=50 /home/agent/tmp/pr8560-908-target-bench/release-with-debug/pr8560-kmeans-bench

The two-thread confirmation used the same binaries with:

LANCE_CPU_THREADS=2 RAYON_NUM_THREADS=2 BENCH_SAMPLES=20 BENCH_WARMUPS=2 BENCH_BATCH=50 BENCH_CASES=default_ivf_high_dim,default_pq_subvector /home/agent/tmp/pr8560-dd0-target-bench/release-with-debug/pr8560-kmeans-bench
LANCE_CPU_THREADS=2 RAYON_NUM_THREADS=2 BENCH_SAMPLES=20 BENCH_WARMUPS=2 BENCH_BATCH=50 BENCH_CASES=default_ivf_high_dim,default_pq_subvector /home/agent/tmp/pr8560-908-target-bench/release-with-debug/pr8560-kmeans-bench

Expected: indexed execution should be selected only for workloads where it matches or improves on the owner rescan. Observed: all indexed committed shapes regressed at 62 threads, and PQ also materially regressed at two threads.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Aug 23, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ci CI / build workflows A-index Vector index, linalg, tokenizer performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KMeans to_kmeans centroid recomputation is suboptimal — each core redundantly scans all data

1 participant