Skip to content

perf: float vectors in a plain table, allocation-free exact scan - #2

Merged
Verdenroz merged 5 commits into
feat/eval-sets-and-store-perffrom
feat/plain-float-vectors
Sep 4, 2026
Merged

Verdenroz merged 5 commits into
feat/eval-sets-and-store-perffrom
feat/plain-float-vectors

Conversation

@Verdenroz

@Verdenroz Verdenroz commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Description

sqlite-vec reads the whole 4 MB vector block for a point lookup, and for a rowid IN set on a KNN query as well, so the two-stage path could never beat exact search by much: 54ms against 68ms at 22k chunks. Floats now live in a plain rowid table and the vec0 float table is gone. The coarse pass rescores 400 candidates in 29ms at recall@10 of 1.000, exact search is a Rust scan that measures 12.0ms at 16k chunks in the soothfast bench, and the file is 151 MB after VACUUM instead of 157.

Changes

  • Added chunk_vectors with ON DELETE CASCADE and a v2 to v3 migration that copies vectors out of vec_chunks and drops it. The copy takes 5.7s on 22k chunks and the file sits at 265 MB until the next VACUUM.
  • Replaced the vec0 exact path with a scan that computes cosine on the blob in place with a bounded top-k, so a full scan allocates nothing per row. The candidate id list for the rescore is built in one allocation.
  • Added repos.chunk_count, maintained by triggers on chunks, in place of the per-query count(*) join.
  • Refused files whose schema_version is newer than the binary, since v3 drops a table an older binary would still query.
  • Made refiner_curve recall distance-based and added recall@10. 1,510 of 22,581 live vectors are exact duplicates, and id-based recall read tie reordering as a 4% loss.

Testing

  • cargo test --workspace: 57 passed, 0 failed. cargo clippy --workspace --all-targets -- -D warnings clean. The migration test now drives a v1 file through to v3 and checks the copied vectors and chunk count.
  • Refiner curve on a fresh copy of the live index, cross-repo: exact 70ms; coarse 100/200/400/800/1600 at 9/16/29/63/75ms with recall@10 of 0.998/1.000/1.000/1.000/1.000 and recall@50 of 0.918/0.980/0.994/0.998/1.000.
  • Eval on a same-moment copy, HyDE off: identical to the base branch on all three sets.
  • cargo soothfast measure -p scry-core --save-baseline base on a quiet machine, and an exact docs/search.md claim that a dense query over 16k chunks allocates at most 11 times. cargo soothfast docs check passes.
  • Deploy order matters: install the new binary and restart the service together. An old binary opening a migrated file fails at query time (vec_chunks is gone); the new binary refuses files newer than itself.

- 1,510 of 22,581 live chunk vectors are exact duplicates, so id-based
  recall read tie reordering as a 4% loss; distance-based recall does not
- vec0 point lookups and IN-filtered KNN both read the whole 4 MB
  vector block per chunk, so the rescore could never beat exact search
- With floats in a rowid table the rescore is 400 4 KB reads: at 22k
  chunks coarse_k 400 takes 29ms for recall@10 1.000 / recall@50 0.994,
  against 98ms exact; exact below the threshold is a Rust scan over the
  same table at parity with vec0
- repos.chunk_count replaces the per-query count(*) join; v2->v3
  migration copies 22k vectors in 8.8s once, file is 151 MB after VACUUM
- Cosine runs on the blob in place with a bounded top-k, so a full scan
  allocates nothing per row: exact at 22k chunks 98ms -> 70ms under the
  same load
- chunk_vectors cascades from chunks and repos.chunk_count is kept by
  triggers, so no Rust path can drift the count or leave a vector behind
- A file newer than this binary's schema is refused at open, since v3
  drops a table an older binary would still query
@Verdenroz Verdenroz changed the title feat/plain float vectors perf: float vectors in a plain table, allocation-free exact scan Sep 4, 2026
- The JSON id list allocated one String per candidate, ~400 of the 415
  allocations in a dense query at 16k chunks; it is now 11
- Alloc claims are exact, so the docs claim states 11, not a ceiling
@Verdenroz
Verdenroz merged commit e59f7d3 into main Sep 4, 2026
2 checks passed
@Verdenroz
Verdenroz deleted the feat/plain-float-vectors branch September 5, 2026 22:02
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.

1 participant