-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Use MaybeUninit in vector storages and query scorers #6303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a new public module, Possibly related PRs
Suggested reviewers
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
lib/segment/src/vector_storage/in_ram_persisted_vectors.rs (1)
95-99
: Method signature improvement for better memory management.This change enhances performance by removing the need to initialize memory that would immediately be overwritten. By accepting a
MaybeUninit
slice, the method avoids unnecessary default initialization of references.Consider adding a brief comment explaining that this function expects the caller to provide an appropriately sized, uninitialized buffer and returns the slice of initialized references.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
lib/common/common/src/lib.rs
(1 hunks)lib/common/common/src/maybe_uninit.rs
(1 hunks)lib/segment/src/vector_storage/chunked_mmap_vectors.rs
(5 hunks)lib/segment/src/vector_storage/chunked_vector_storage.rs
(2 hunks)lib/segment/src/vector_storage/dense/appendable_dense_vector_storage.rs
(2 hunks)lib/segment/src/vector_storage/dense/memmap_dense_vector_storage.rs
(2 hunks)lib/segment/src/vector_storage/dense/mmap_dense_vectors.rs
(2 hunks)lib/segment/src/vector_storage/in_ram_persisted_vectors.rs
(2 hunks)lib/segment/src/vector_storage/multi_dense/appendable_mmap_multi_dense_vector_storage.rs
(0 hunks)lib/segment/src/vector_storage/multi_dense/simple_multi_dense_vector_storage.rs
(1 hunks)lib/segment/src/vector_storage/quantized/quantized_custom_query_scorer.rs
(0 hunks)lib/segment/src/vector_storage/quantized/quantized_query_scorer.rs
(0 hunks)lib/segment/src/vector_storage/query_scorer/custom_query_scorer.rs
(2 hunks)lib/segment/src/vector_storage/query_scorer/metric_query_scorer.rs
(2 hunks)lib/segment/src/vector_storage/query_scorer/mod.rs
(2 hunks)lib/segment/src/vector_storage/query_scorer/multi_custom_query_scorer.rs
(2 hunks)lib/segment/src/vector_storage/query_scorer/multi_metric_query_scorer.rs
(2 hunks)lib/segment/src/vector_storage/query_scorer/sparse_custom_query_scorer.rs
(0 hunks)lib/segment/src/vector_storage/vector_storage_base.rs
(4 hunks)
💤 Files with no reviewable changes (4)
- lib/segment/src/vector_storage/quantized/quantized_query_scorer.rs
- lib/segment/src/vector_storage/query_scorer/sparse_custom_query_scorer.rs
- lib/segment/src/vector_storage/multi_dense/appendable_mmap_multi_dense_vector_storage.rs
- lib/segment/src/vector_storage/quantized/quantized_custom_query_scorer.rs
🧰 Additional context used
🧬 Code Definitions (5)
lib/segment/src/vector_storage/query_scorer/mod.rs (4)
lib/segment/src/vector_storage/query_scorer/custom_query_scorer.rs (1)
score_stored_batch
(92-106)lib/segment/src/vector_storage/query_scorer/metric_query_scorer.rs (1)
score_stored_batch
(73-88)lib/segment/src/vector_storage/query_scorer/multi_custom_query_scorer.rs (1)
score_stored_batch
(129-147)lib/segment/src/vector_storage/query_scorer/multi_metric_query_scorer.rs (1)
score_stored_batch
(111-129)
lib/segment/src/vector_storage/in_ram_persisted_vectors.rs (2)
lib/segment/src/vector_storage/chunked_mmap_vectors.rs (2)
get_batch
(299-316)get_batch
(405-411)lib/segment/src/vector_storage/chunked_vector_storage.rs (1)
get_batch
(51-55)
lib/segment/src/vector_storage/chunked_vector_storage.rs (2)
lib/segment/src/vector_storage/chunked_mmap_vectors.rs (2)
get_batch
(299-316)get_batch
(405-411)lib/segment/src/vector_storage/in_ram_persisted_vectors.rs (1)
get_batch
(95-101)
lib/segment/src/vector_storage/dense/appendable_dense_vector_storage.rs (3)
lib/segment/src/vector_storage/vector_storage_base.rs (2)
std
(141-141)get_dense_batch
(132-138)lib/common/common/src/maybe_uninit.rs (1)
maybe_uninit_fill_from
(10-33)lib/segment/src/vector_storage/dense/memmap_dense_vector_storage.rs (1)
get_dense_batch
(144-151)
lib/segment/src/vector_storage/chunked_mmap_vectors.rs (2)
lib/common/common/src/maybe_uninit.rs (1)
maybe_uninit_fill_from
(10-33)lib/segment/src/vector_storage/chunked_vector_storage.rs (2)
get_batch
(51-55)len
(15-15)
⏰ Context from checks skipped due to timeout of 90000ms (13)
- GitHub Check: test-shard-snapshot-api-s3-minio
- GitHub Check: test-snapshot-operations-s3-minio
- GitHub Check: test-consistency
- GitHub Check: test-consensus
- GitHub Check: test-low-resources
- GitHub Check: test (macos-latest)
- GitHub Check: test
- GitHub Check: test (windows-latest)
- GitHub Check: test-consensus-compose
- GitHub Check: test
- GitHub Check: test
- GitHub Check: test (ubuntu-latest)
- GitHub Check: Basic TLS/HTTPS tests
🔇 Additional comments (36)
lib/common/common/src/lib.rs (1)
15-15
: Looks good: New module for optimizing vector initialization.The addition of the
maybe_uninit
module aligns with the PR's objective to optimize performance by eliminating unnecessary vector initialization overhead. This module likely contains the backported version ofMaybeUninit::fill_from
mentioned in the PR description.lib/segment/src/vector_storage/query_scorer/custom_query_scorer.rs (2)
3-3
: Appropriate import for MaybeUninit.The import of
MaybeUninit
is necessary for the optimization changes that follow.
96-99
: Performance improvement: Using uninitialized memory.This implementation now uses
MaybeUninit
to avoid the overhead of initializing the temporary array with default values before it gets populated with actual data. This matches the PR objective of optimizing initialization of temporary arrays used during vector operations.lib/segment/src/vector_storage/multi_dense/simple_multi_dense_vector_storage.rs (1)
25-25
:✅ Verification successful
Note the deliberate removal of batch method.
Based on the AI summary, the
get_batch_multi
method has been completely removed from this implementation. This is part of the broader changes to optimize batch operations by usingMaybeUninit
elsewhere. The import has been updated to only include what's actually used in the file.
🏁 Script executed:
#!/bin/bash # Check for any remaining references to get_batch_multi in this file rg "get_batch_multi" lib/segment/src/vector_storage/multi_dense/simple_multi_dense_vector_storage.rsLength of output: 100
Removal Verified: The deliberate elimination of
get_batch_multi
is confirmed
- The search command produced no matches in
lib/segment/src/vector_storage/multi_dense/simple_multi_dense_vector_storage.rs
, indicating thatget_batch_multi
is no longer present.- The updated import now correctly includes only
CHUNK_SIZE
andStoredRecord
, aligning with the optimization strategy utilizingMaybeUninit
for batch operations.lib/segment/src/vector_storage/query_scorer/mod.rs (2)
8-8
: Necessary import for the default implementation.Adding the
VECTOR_READ_BATCH_SIZE
import is necessary for the new default implementation ofscore_stored_batch
.
22-30
: Good addition: Default implementation for batch scoring.Adding a default implementation for
score_stored_batch
provides a fallback for implementations that don't need specialized batch processing. This is a sensible change that:
- Ensures all implementations have this method available
- Maintains the batch size constraints with assertions
- Provides a simple implementation that calls
score_stored
for each ID individuallyThe implementation is correct and aligns with the batch processing pattern seen in the specialized implementors of this trait, while allowing custom optimizations in specific implementations.
lib/segment/src/vector_storage/in_ram_persisted_vectors.rs (1)
1-1
: Good addition of necessary import.The import of
MaybeUninit
is needed for the changed function signature below.lib/common/common/src/maybe_uninit.rs (1)
1-33
: Well-implemented backport ofMaybeUninit::fill_from
functionality.The implementation follows good practices:
- Clear documentation with usage constraints
- Properly annotated safety comments
- Explicit assertion for unsupported types
- Efficient memory handling
The code correctly:
- Fills uninitialized memory with values from an iterator
- Tracks the number of initialized elements
- Returns both the initialized portion and the remainder
One minor observation:
const { assert!(!std::mem::needs_drop::<I::Item>(), "Not supported") };This compile-time assertion prevents usage with types that need dropping. This is a reasonable limitation for a simplified backport, and it's well-documented.
lib/segment/src/vector_storage/dense/memmap_dense_vector_storage.rs (2)
4-4
: Good addition of necessary import.The import of
MaybeUninit
is correctly added for the modified function signature.
144-151
: Method signature improvement for better memory management.This change enhances performance by removing the need to initialize memory that would immediately be overwritten. The method now follows the pattern of accepting uninitialized memory and returning properly initialized values.
The implementation correctly delegates to the
mmap_store.get_vectors
method with the updated signature.lib/segment/src/vector_storage/dense/mmap_dense_vectors.rs (3)
3-3
: Good addition of necessary import.The import of
MaybeUninit
is correctly added for the modified function signatures.
9-9
: Good addition of helper function import.The import of
maybe_uninit_fill_from
is appropriately added to support the new implementation approach.
160-177
: Excellent refactoring to improve memory usage efficiency.This implementation:
- Properly updates the function signature to use
MaybeUninit
- Uses the new
maybe_uninit_fill_from
helper function to efficiently populate vectors- Maintains the same logic branches (optimized path for sequential reads)
- Returns the properly initialized slice
The refactoring eliminates unnecessary default initialization of memory that would be immediately overwritten, which should improve performance especially for batch operations with small vectors as mentioned in the PR objectives.
I particularly like how the two code paths (with and without prefetching) both use the same pattern, making the code more consistent and easier to understand.
lib/segment/src/vector_storage/query_scorer/metric_query_scorer.rs (2)
3-3
: Added MaybeUninit import for optimized vector initialization.
77-81
: Improved memory efficiency by using uninitialized memory.The function now properly uses
MaybeUninit
to avoid unnecessary initialization of vector values that would immediately be overwritten. This implementation optimizes the performance, especially when working with small vectors, as mentioned in the PR objectives.lib/segment/src/vector_storage/query_scorer/multi_custom_query_scorer.rs (5)
2-2
: Added MaybeUninit import for optimized vector initialization.
130-131
: Improved assertions for batch processing.The assertions have been simplified to directly check
ids.len()
rather than calculating a separate batch size variable, making the code more direct and easier to understand.
133-136
: Optimized memory initialization for batch vector retrieval.Using
MaybeUninit
to allocate uninitialized memory for vectors improves performance by eliminating the unnecessary initialization of default values that would be immediately overwritten.
138-138
: Improved calculation of total loaded vectors.The code now directly iterates over the vectors array to calculate the total number of loaded vectors, which is more concise and eliminates the need for slicing.
144-144
: Simplified batch processing loop.The loop now directly uses
ids.len()
instead of a separate batch size variable, making the code more straightforward.lib/segment/src/vector_storage/query_scorer/multi_metric_query_scorer.rs (5)
2-2
: Added MaybeUninit import for optimized vector initialization.
112-113
: Improved assertions for batch processing.The assertions have been simplified to directly check
ids.len()
rather than calculating a separate batch size variable, making the code more direct and easier to understand.
115-118
: Optimized memory initialization for batch vector retrieval.Using
MaybeUninit
to allocate uninitialized memory for vectors improves performance by eliminating the unnecessary initialization of default values that would be immediately overwritten.
120-120
: Improved calculation of total read vectors.The code now directly iterates over the vectors array to calculate the total number of read vectors, which is more concise and eliminates the need for slicing.
126-126
: Simplified batch processing loop.The loop now directly uses
ids.len()
instead of a separate batch size variable, making the code more straightforward.lib/segment/src/vector_storage/dense/appendable_dense_vector_storage.rs (2)
3-3
: Added MaybeUninit and helper function import.The code now imports both
MaybeUninit
for uninitialized memory and themaybe_uninit_fill_from
helper function that was introduced in this PR to efficiently work with uninitialized memory.Also applies to: 10-10
73-85
: Optimized batch vector retrieval with MaybeUninit.The
get_dense_batch
method has been rewritten to useMaybeUninit
for both the input parameter and the internal vector offset handling, which eliminates unnecessary initialization of memory. This implementation is now consistent with the similar code in other storage implementations, as seen in the relevant code snippets frommemmap_dense_vector_storage.rs
andvector_storage_base.rs
.Key improvements:
- Changed parameter type to use
MaybeUninit<&'a [T]>
instead of initialized references- Uses uninitialized memory for vector offsets
- Efficiently fills the offsets using the new
maybe_uninit_fill_from
helperThis approach aligns with the PR objective of optimizing performance, particularly for operations involving small vectors.
lib/segment/src/vector_storage/chunked_vector_storage.rs (2)
1-1
: Good use of MaybeUninit for optimized initialization.Adding this import supports the new optimized approach for handling uninitialized memory in batch operations.
51-55
: Well-designed API change for enhanced performance.The signature change for
get_batch
is a good optimization that eliminates unnecessary initialization of vectors. By usingMaybeUninit
, the method now:
- Takes uninitialized memory and populates it directly
- Returns a reference to the initialized portion
- Avoids the overhead of initializing the array with default values
This aligns perfectly with the PR objective of enhancing performance in batch operations.
lib/segment/src/vector_storage/vector_storage_base.rs (3)
1-1
: Added imports support the optimization approach.The added imports are necessary to support the new optimization pattern using
MaybeUninit
and the helper functionmaybe_uninit_fill_from
.Also applies to: 8-8, 29-29
132-138
: Default implementation improved for DenseVectorStorage.The default implementation of
get_dense_batch
now usesMaybeUninit
andmaybe_uninit_fill_from
to efficiently initialize the vectors array. The new implementation:
- Takes uninitialized memory as input
- Populates it with vector references
- Returns only the initialized portion
This is more efficient than initializing with default values first and offers a better default implementation for implementors of this trait.
154-162
: MultiVectorStorage batch implementation optimized.The
get_batch_multi
implementation now uses the same optimization pattern with these key improvements:
- Changed parameter to use
MaybeUninit
for uninitialized memory- Added proper debug assertions to validate inputs
- Used
maybe_uninit_fill_from
for safe initialization- Returns the initialized portion
The debug assertions ensure that the vectors and keys arrays have matching lengths and that we don't exceed the batch size limit, which is good for catching errors early in development.
lib/segment/src/vector_storage/chunked_mmap_vectors.rs (4)
3-3
: Good addition of necessary imports.These imports support the new optimization pattern using
MaybeUninit
and the helper functionmaybe_uninit_fill_from
.Also applies to: 7-7
299-316
: Optimized batch retrieval implementation.The
get_batch
implementation has been improved to useMaybeUninit
for more efficient memory handling. The key improvements are:
- Changed the function signature to use
MaybeUninit
and return the initialized results- Maintained the same debug assertions to verify input correctness
- Used
maybe_uninit_fill_from
to populate vectors directly without prior initialization- Added proper error handling with panic messages when vectors aren't found
This improves performance by eliminating the unnecessary initialization step while maintaining safety.
405-411
: Trait implementation aligned with optimization pattern.The trait implementation for
get_batch
has been updated to match the new method signature and properly delegates to theChunkedMmapVectors::get_batch
method. This maintains consistency across the codebase.
467-478
: Test code properly updated to use new pattern.The test code has been updated to:
- Use
MaybeUninit::uninit()
for buffer initialization instead of pre-filled values- Capture the return value from
get_batch
which now returns the initialized vectors- Maintain the same validation logic to ensure correctness
This validates that the new implementation works correctly while testing the performance optimization.
Inside batch getters/scorers, the following approach is common:
Filling temporary array with default values is excessive as these values would be overwritten anyway; but it takes some amount of CPU cycles. If we decide to use batch score methods during search (as suggested in #6245 (comment)), we'll lose a few percents of performance during search on small vectors. To win this performance back, we need to avoid initializing values.
In this PR, these temporary arrays are not initialized, like this:
MaybeUninit::fill_from
would be useful here, but it's not in the stable; so, I've backported a simplified version of it in this PR. An alternative option is to use theuninit::Out::fill_with_iter
, but I decided not to pull additional dependency for that.