Repository navigation
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Describe the changes in the pull request
HNSW resizes and copies its auxiliary arrays at every graph-block boundary. This draft doubles auxiliary capacity when exhausted and shrinks it to twice the live count at one-quarter utilization. Graph and vector storage still grow in blocks. The existing resize helper, insertion locks and allocation-failure behavior are unchanged; existing allocator accounting includes the spare capacity.
The production diff is confined to
hnsw.h: growth/shrink policy, the corresponding capacity assertion, and test-onlyfitMemory()compaction. Existing tests cover small-block growth/deletion/regrowth, empty reuse, serialization restoration and tiered resizing.Benchmark results
The patch insertion loop took 44.09–44.20 minutes versus 50.64–52.26 minutes
for baseline.
The earlier, larger prototype on base
ec835be8cshowed 14.6–16.5% reductionswith the same C++ workload. The minimal patch retains a similar measured benefit;
these separate campaigns do not establish identical performance.
The extra index allocation was 142,188,880 bytes, or 135.60 MiB (3.13%). This is
allocator accounting at 10M, not process RSS or peak allocation during resizing.
Sampled query signatures at 1M, 2M, 4M, 8M and 10M matched across all four runs;
each checkpoint used 32 self queries. Every process exited successfully. The
artifact validator checked all four runs, their expected resize counts and probes,
query signatures, and agreement between checkpoint and Google Benchmark timings.
At the selected insertion crossing 8,388,608 vectors, median complete insertion
time was 71.919 ms for baseline and 702.122 ms for the patch (two samples each).
At 32 nearby block boundaries per run, median complete insertion time was
71.350 ms and 0.282 ms respectively (64 samples each). Less frequent resizing
therefore does not eliminate large individual growth events.
Benchmark setup, validation, and limitations
Workload and provenance
fcdeb37b1c264c00ad2e8176d91cc2c0d071625a.9a3ca0a55e5331afd13382678c04db5602ec2ee9.-O3, assertions enabled, Intel Xeon Platinum 8375C.used a different core.
Benchmark case. Input generation, index construction, checkpoint queries,
checkpoint RSS collection and output were excluded from manual insertion-loop
wall time. Equal loop bookkeeping and selected insertion timing probes were
included.
Both builds used the same harness and resize-helper timer. Tracked source changes
against each pinned revision, including staged changes, were checked: the only
instrumentation edit added that timer to
resizeIndexCommon. Both executablehashes were unchanged after all runs. Both builds used an identical external
CMake hook setting
HAVE_SVS_LVQ=0. That build setting is not part of the patch;these measurements do not validate the default SVS dependency configuration.
Benchmark validation and limits
Before measurement, both instrumented variants passed a 32,769-vector smoke run
with matching queries and an 8,193-vector Valgrind run with zero errors and no
definitely, indirectly or possibly lost blocks. Each retained 329 bytes in five
reachable blocks. Two unchanged-baseline controls at 100K took 10.646 s and
10.500 s; these short controls do not bound noise in the 10M measurements.
No swap activity was recorded during the runs. Host load does not measure
contention on the pinned CPU. These are descriptive observations from two runs
per variant on a shared host, not a confidence interval or a causal estimate of
every saved second.
This generated single-index workload does not reproduce a representative sharded
deployment. Selected insertion durations and resize-helper time do not measure
Redis command latency, reader wait time or exclusive-lock duration. Matching
sampled queries do not establish full ANN recall.
Before marking the draft ready, measure concurrent-query latency during ingestion
at representative local index sizes and vector dimensions, and confirm standard
CI with the default SVS dependency configuration. Larger speculative allocations
can fail earlier under memory pressure; allocation-failure recovery is not
addressed by this capacity-policy change.
Validation
test_hnsw: 367;test_allocator: 7;test_hnsw_sq8: 145).git diff --checkpassed.The validation and benchmark builds used
HAVE_SVS_LVQ=0via an external CMake hook. That build setting is not included in this patch; the default SVS dependency configuration still needs standard CI validation.Before marking ready
Which issues this PR fixes
No linked issue; this addresses repeated whole-array auxiliary resizing during HNSW ingestion.
Main objects this PR modified
Mark if applicable