Skip to content

Make build RAM estimation store-owned - #1277

Open
wuw92 wants to merge 2 commits into
wuw92/refactor-disk-build-structure-mainfrom
wuw92/store-owned-ram-estimate
Open

Make build RAM estimation store-owned#1277
wuw92 wants to merge 2 commits into
wuw92/refactor-disk-build-structure-mainfrom
wuw92/store-owned-ram-estimate

Conversation

@wuw92

@wuw92 wuw92 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Make build RAM estimation store-owned

Stacked on #1271 — review against wuw92/refactor-disk-build-structure-main.

Why

Addresses #1271 (comment).

The build RAM estimate was a hand-written byte formula with no link to the stores it described, so layout changes silently invalidated it.

What

Make each store answer for its own size, so an allocation and its estimate cannot diverge. Every estimate mirrors one constructor at its own level and knows only what that constructor directly builds:

DefaultProviderParameters::estimated_bytes        mirrors  new_empty
  ├─ CU/CV/CD: EstimateBytes                      mirrors  CreateVectorStore::create
  │    ├─ CreateFullPrecision<T> ─┐
  │    ├─ FixedChunkPQTable       ├─ <Store>::estimate_bytes   mirrors  <Store>::new
  │    ├─ WithBits<NBITS>       ──┘
  │    └─ NoStore / NoDeletes → 0
  └─ SimpleNeighborProviderAsync::estimate_bytes   mirrors  SimpleNeighborProviderAsync::new
       └─ AlignedMemoryVectorStore::estimate_bytes mirrors  with_capacity

The estimator moves beside the in-memory builder.
Scoped to the stores disk build uses; small or fixed overheads such as PQ pivots stay uncounted.

Also removes the graph_slack_factor parameter from SimpleNeighborProviderAsync: slack is already folded into max_degree, so every call site passed 1.0.

`estimate_build_index_ram_usage` hand-wrote a byte formula with no link to the
data structures it was describing, so layout changes silently invalidated it.
It had already drifted: the graph term applied `GRAPH_SLACK_FACTOR` on top of a
`max_degree` that already includes slack, and it ignored the per-row length
prefix, 64-byte alignment padding, frozen points, and the per-point lock
vectors.

Each store now exposes a sizing helper that its own constructor consumes, so
allocation and estimation cannot diverge:

- `AlignedMemoryVectorStore::layout` is the single source of truth for the
  backing allocation; `with_capacity` and `estimate_bytes` both derive from it.
- `SimpleNeighborProviderAsync`, `FastMemoryVectorProviderAsync`,
  `FastMemoryQuantVectorProviderAsync`, and `SQStore` share their row-width and
  lock-count computations between `new` and `estimate_bytes`.
- A narrow `EstimateBytes` trait is implemented for the precursors disk build
  actually uses, and `DefaultProviderParameters::estimated_bytes` aggregates
  them using the same `npts` and start-point constants as `new_empty`.

The estimator and the `provider_parameters` helper move to `inmem_builder.rs`,
next to `new_inmem_index_builder`, whose `match` over `BuildQuantizer` the
estimator must mirror. This also removes `build.rs`'s dependency on
`merged_index.rs`, which existed only to borrow the estimator.

The partition-time estimate sizes against the shard graph config it now shares
with `create_shard_index_config`.

This is an estimate, not an accounting: allocations that scale with the point
count are counted, fixed overheads such as PQ pivots and quantizer parameters
are not. Build strategy thresholds shift accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 482d6098-133f-48d7-8c5b-317dae385fe7
Slack is already resolved when the index configuration is built:
config::MaxDegree::Slack folds the factor into the final max_degree, and
DefaultProviderParameters::max_degree is documented as the actual maximum
number of neighbors. Every call site therefore passed 1.0.

Keeping the parameter is what let the estimate and the allocation drift
apart in the first place, and applying slack twice would silently
over-allocate each adjacency list without tripping the length assert in
set_neighbors_sync. Dropping it makes new and estimate_bytes agree by
construction rather than by both being handed the same constant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4d98f122-fb99-47d3-88f9-9d257a55c8c8
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