Skip to content

Commit 24c5bed

Browse files
authored
Merge: Accurate memory statistics (#717)
Closes #608
2 parents 5cde3fc + d5a04f8 commit 24c5bed

File tree

5 files changed

+79
-13
lines changed

5 files changed

+79
-13
lines changed

include/usearch/index_dense.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,37 @@ class index_dense_gt {
746746
vectors_tape_allocator_.total_allocated();
747747
}
748748

749+
/**
750+
* @brief Aggregated memory statistics for the allocator tapes used by the dense index.
751+
*/
752+
struct memory_stats_t {
753+
/// Memory stats for the graph structure allocator.
754+
std::size_t graph_allocated;
755+
std::size_t graph_wasted;
756+
std::size_t graph_reserved;
757+
/// Memory stats for the vectors data allocator.
758+
std::size_t vectors_allocated;
759+
std::size_t vectors_wasted;
760+
std::size_t vectors_reserved;
761+
};
762+
763+
/**
764+
* @brief Returns detailed memory statistics with separate breakdowns for the graph
765+
* and vectors allocator tapes.
766+
* @return A `memory_stats_t` struct with per-tape allocated, wasted, and reserved bytes.
767+
*/
768+
memory_stats_t memory_stats() const {
769+
auto const& graph_alloc = typed_->tape_allocator();
770+
return {
771+
graph_alloc.total_allocated(),
772+
graph_alloc.total_wasted(),
773+
graph_alloc.total_reserved(),
774+
vectors_tape_allocator_.total_allocated(),
775+
vectors_tape_allocator_.total_wasted(),
776+
vectors_tape_allocator_.total_reserved(),
777+
};
778+
}
779+
749780
static constexpr std::size_t any_thread() { return std::numeric_limits<std::size_t>::max(); }
750781
static constexpr distance_t infinite_distance() { return std::numeric_limits<distance_t>::max(); }
751782

include/usearch/index_plugins.hpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,7 @@ template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
889889
std::size_t last_usage_ = head_size();
890890
std::size_t last_capacity_ = min_capacity();
891891
std::size_t wasted_space_ = 0;
892+
std::size_t total_allocated_ = 0;
892893

893894
public:
894895
using value_type = byte_t;
@@ -899,13 +900,15 @@ template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
899900
memory_mapping_allocator_gt() = default;
900901
memory_mapping_allocator_gt(memory_mapping_allocator_gt&& other) noexcept
901902
: last_arena_(exchange(other.last_arena_, nullptr)), last_usage_(exchange(other.last_usage_, 0)),
902-
last_capacity_(exchange(other.last_capacity_, 0)), wasted_space_(exchange(other.wasted_space_, 0)) {}
903+
last_capacity_(exchange(other.last_capacity_, 0)), wasted_space_(exchange(other.wasted_space_, 0)),
904+
total_allocated_(exchange(other.total_allocated_, 0)) {}
903905

904906
memory_mapping_allocator_gt& operator=(memory_mapping_allocator_gt&& other) noexcept {
905907
std::swap(last_arena_, other.last_arena_);
906908
std::swap(last_usage_, other.last_usage_);
907909
std::swap(last_capacity_, other.last_capacity_);
908910
std::swap(wasted_space_, other.wasted_space_);
911+
std::swap(total_allocated_, other.total_allocated_);
909912
return *this;
910913
}
911914

@@ -930,6 +933,7 @@ template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
930933
last_usage_ = head_size();
931934
last_capacity_ = min_capacity();
932935
wasted_space_ = 0;
936+
total_allocated_ = 0;
933937
}
934938

935939
/**
@@ -968,6 +972,7 @@ template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
968972
last_arena_ = new_arena;
969973
last_capacity_ = new_cap;
970974
last_usage_ = head_size();
975+
total_allocated_ += new_cap;
971976
}
972977

973978
wasted_space_ += extended_bytes - count_bytes;
@@ -978,17 +983,7 @@ template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
978983
* @brief Returns the amount of memory used by the allocator across all arenas.
979984
* @return The amount of space in bytes.
980985
*/
981-
std::size_t total_allocated() const noexcept {
982-
if (!last_arena_)
983-
return 0;
984-
std::size_t total_used = 0;
985-
std::size_t last_capacity = last_capacity_;
986-
do {
987-
total_used += last_capacity;
988-
last_capacity /= capacity_multiplier();
989-
} while (last_capacity >= min_capacity());
990-
return total_used;
991-
}
986+
std::size_t total_allocated() const noexcept { return total_allocated_; }
992987

993988
/**
994989
* @brief Returns the amount of wasted space due to alignment.

rust/lib.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@ void NativeIndex::view(rust::Str path) const {
180180

181181
void NativeIndex::reset() const { index_->reset(); }
182182
size_t NativeIndex::memory_usage() const { return index_->memory_usage(); }
183+
184+
MemoryStats NativeIndex::memory_stats() const {
185+
auto stats = index_->memory_stats();
186+
MemoryStats result;
187+
result.graph_allocated = stats.graph_allocated;
188+
result.graph_wasted = stats.graph_wasted;
189+
result.graph_reserved = stats.graph_reserved;
190+
result.vectors_allocated = stats.vectors_allocated;
191+
result.vectors_wasted = stats.vectors_wasted;
192+
result.vectors_reserved = stats.vectors_reserved;
193+
return result;
194+
}
195+
183196
char const* NativeIndex::hardware_acceleration() const { return index_->metric().isa_name(); }
184197

185198
void NativeIndex::save_to_buffer(rust::Slice<uint8_t> buffer) const {

rust/lib.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// We don't have to forward declare all of those:
55
struct Matches;
66
struct IndexOptions;
7+
struct MemoryStats;
78
enum class MetricKind;
89
enum class ScalarKind;
910

@@ -83,6 +84,7 @@ class NativeIndex {
8384
void view(rust::Str path) const;
8485
void reset() const;
8586
size_t memory_usage() const;
87+
MemoryStats memory_stats() const;
8688
char const* hardware_acceleration() const;
8789

8890
void save_to_buffer(rust::Slice<uint8_t> buffer) const;

rust/lib.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,24 @@ pub mod ffi {
301301
distances: Vec<f32>,
302302
}
303303

304+
/// Detailed memory statistics with separate breakdowns for the graph
305+
/// and vectors allocator tapes.
306+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
307+
struct MemoryStats {
308+
/// Total memory allocated by the graph structure allocator, in bytes.
309+
graph_allocated: usize,
310+
/// Memory wasted due to alignment in the graph allocator, in bytes.
311+
graph_wasted: usize,
312+
/// Reserved but unused memory in the graph allocator, in bytes.
313+
graph_reserved: usize,
314+
/// Total memory allocated by the vectors data allocator, in bytes.
315+
vectors_allocated: usize,
316+
/// Memory wasted due to alignment in the vectors allocator, in bytes.
317+
vectors_wasted: usize,
318+
/// Reserved but unused memory in the vectors allocator, in bytes.
319+
vectors_reserved: usize,
320+
}
321+
304322
/// The index options used to configure the dense index during creation.
305323
/// It contains the number of dimensions, the metric kind, the scalar kind,
306324
/// the connectivity, the expansion values, and the multi-flag.
@@ -423,6 +441,7 @@ pub mod ffi {
423441
pub fn view(self: &NativeIndex, path: &str) -> Result<()>;
424442
pub fn reset(self: &NativeIndex) -> Result<()>;
425443
pub fn memory_usage(self: &NativeIndex) -> usize;
444+
pub fn memory_stats(self: &NativeIndex) -> MemoryStats;
426445
pub fn hardware_acceleration(self: &NativeIndex) -> *const c_char;
427446

428447
pub fn save_to_buffer(self: &NativeIndex, buffer: &mut [u8]) -> Result<()>;
@@ -432,7 +451,7 @@ pub mod ffi {
432451
}
433452

434453
// Re-export the FFI structs and enums at the crate root for easy access
435-
pub use ffi::{IndexOptions, MetricKind, ScalarKind};
454+
pub use ffi::{IndexOptions, MemoryStats, MetricKind, ScalarKind};
436455

437456
/// Represents custom metric functions for calculating distances between vectors in various formats.
438457
///
@@ -1376,6 +1395,12 @@ impl Index {
13761395
self.inner.memory_usage()
13771396
}
13781397

1398+
/// Returns detailed memory statistics with separate breakdowns for the graph
1399+
/// and vectors allocator tapes.
1400+
pub fn memory_stats(self: &Index) -> ffi::MemoryStats {
1401+
self.inner.memory_stats()
1402+
}
1403+
13791404
/// Saves the index to a specified file.
13801405
///
13811406
/// # Arguments

0 commit comments

Comments
 (0)