Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion docs/docs/en/src/advanced/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,27 @@ if (index->CheckFeature(vsag::SUPPORT_ESTIMATE_MEMORY)) {

See `examples/cpp/308_feature_estimate_memory.cpp` for a full run.

### `EstimateBuildMemory(num_elements)`

`Index::EstimateBuildMemory(num_elements)` returns the estimated memory (in bytes) required
**during the build process** for `num_elements` vectors. Unlike `EstimateMemory`, which
estimates the steady-state size of the final index, this accounts for temporary buffers and
intermediate data structures that exist only while `Build` is running. The peak memory during
build is typically higher than the post-build footprint:

```cpp
uint64_t peak = index->EstimateBuildMemory(1000000); // bytes
```

Currently only DiskANN provides a non-trivial implementation; other index types throw
an exception by default.

### `GetMemoryUsage()`

`Index::GetMemoryUsage()` returns the **current** memory footprint of an index in bytes:

```cpp
int64_t bytes = index->GetMemoryUsage();
uint64_t bytes = index->GetMemoryUsage();
```

Properties:
Expand All @@ -86,6 +101,26 @@ Properties:
See `examples/cpp/319_feature_get_memory_usage.cpp` for a runnable example, including a helper
that compares the interface value with the process resident size.

### `GetMemoryUsageDetail()`

`Index::GetMemoryUsageDetail()` returns a breakdown of the **current** memory usage by
component:

```cpp
std::unordered_map<std::string, uint64_t> detail = index->GetMemoryUsageDetail();
for (const auto& [component, bytes] : detail) {
std::cout << component << ": " << bytes << " bytes\n";
}
```

The returned map keys are component names and values are memory in bytes. This is useful for
understanding *where* the memory is going inside an index.

Currently only HGraph provides a meaningful implementation, returning components such as
`basic_flatten_codes`, `bottom_graph`, `route_graph`, `neighbors_mutex`, `pool`,
`label_table`, `high_precise_codes`, `extra_infos`, and `raw_vector`. SINDI returns an empty
map. Other index types throw an exception by default.

### Capability Flags

| Flag | Meaning |
Expand Down
31 changes: 30 additions & 1 deletion docs/docs/zh/src/advanced/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,24 @@ if (index->CheckFeature(vsag::SUPPORT_ESTIMATE_MEMORY)) {

完整示例:`examples/cpp/308_feature_estimate_memory.cpp`。

### `EstimateBuildMemory(num_elements)`

`Index::EstimateBuildMemory(num_elements)` 返回构建 `num_elements` 条向量的索引时**构建过程中**
所需的预估内存(字节数)。与 `EstimateMemory`(估算最终索引的稳态大小)不同,该接口考虑了构建
过程中仅临时存在的缓冲区与中间数据结构。构建期间的峰值内存通常高于构建完成后的内存占用:

```cpp
uint64_t peak = index->EstimateBuildMemory(1000000); // 字节
```

目前仅 DiskANN 提供了有效实现,其他索引类型默认抛出异常。

### `GetMemoryUsage()`

`Index::GetMemoryUsage()` 返回索引**当前**占用的字节数:

```cpp
int64_t bytes = index->GetMemoryUsage();
uint64_t bytes = index->GetMemoryUsage();
```

特性:
Expand All @@ -80,6 +92,23 @@ int64_t bytes = index->GetMemoryUsage();
可运行示例:`examples/cpp/319_feature_get_memory_usage.cpp`,其中包含一个辅助函数将接口值与进程
驻留内存进行对照。

### `GetMemoryUsageDetail()`

`Index::GetMemoryUsageDetail()` 返回索引**当前**内存占用按组件的细分:

```cpp
std::unordered_map<std::string, uint64_t> detail = index->GetMemoryUsageDetail();
for (const auto& [component, bytes] : detail) {
std::cout << component << ": " << bytes << " bytes\n";
}
```

返回的 map 的 key 为组件名,value 为对应内存字节数。该接口有助于了解索引内部的内存分布。

目前仅 HGraph 提供了有效实现,返回的组件包括 `basic_flatten_codes`、`bottom_graph`、
`route_graph`、`neighbors_mutex`、`pool`、`label_table`、`high_precise_codes`、
`extra_infos` 和 `raw_vector`。SINDI 返回空 map,其他索引类型默认抛出异常。

### 能力标志

| 标志 | 含义 |
Expand Down
16 changes: 7 additions & 9 deletions include/vsag/index.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <stdexcept>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -900,18 +901,15 @@ class Index {
*
* @return number of bytes occupied by the index.
*/
[[nodiscard]] virtual int64_t
[[nodiscard]] virtual uint64_t
GetMemoryUsage() const = 0;

/**
* @brief Return the memory usage of every component in the index
*
* @return a json object that contains the memory usage of every component in the index
* @return a map from component name to memory usage in bytes
*/
// TODO(deming): implement func for every types of index
// [[nodiscard]] virtual JsonType
// GetMemoryUsageDetail() const = 0;
[[nodiscard]] virtual std::string
[[nodiscard]] virtual std::unordered_map<std::string, uint64_t>
GetMemoryUsageDetail() const {
Comment thread
LHT129 marked this conversation as resolved.
throw std::runtime_error("Index does not support GetMemoryUsageDetail");
}
Comment thread
LHT129 marked this conversation as resolved.
Comment thread
LHT129 marked this conversation as resolved.
Comment thread
LHT129 marked this conversation as resolved.
Expand All @@ -933,9 +931,9 @@ class Index {
* @param num_elements denotes the amount of data used to build the index.
* @return estimated memory required during building.
*/
[[nodiscard]] virtual int64_t
GetEstimateBuildMemory(const int64_t num_elements) const {
throw std::runtime_error("Index does not support estimate the memory while building");
[[nodiscard]] virtual uint64_t
EstimateBuildMemory(uint64_t num_elements) const {
throw std::runtime_error("Index does not support EstimateBuildMemory");
}
Comment thread
LHT129 marked this conversation as resolved.

/**
Expand Down
2 changes: 1 addition & 1 deletion include/vsag/vsag_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class IndexHandler {
int64_t
GetNumElements() const;

int64_t
uint64_t
GetMemoryUsage() const;

private:
Expand Down
2 changes: 1 addition & 1 deletion mockimpl/vsag/simpleflat.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class SimpleFlat : public Index {
tl::expected<void, Error>
Deserialize(const ReaderSet& reader_set) override;

int64_t
uint64_t
GetMemoryUsage() const override {
size_t ids_size = num_elements_ * sizeof(int64_t);
size_t vector_size = num_elements_ * dim_ * sizeof(float);
Expand Down
4 changes: 2 additions & 2 deletions src/algorithm/bruteforce/bruteforce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,9 +1044,9 @@ BruteForce::cal_memory_usage() {
this->current_memory_usage_.store(memory_usage);
}

int64_t
uint64_t
BruteForce::GetMemoryUsage() const {
int64_t memory = 0;
uint64_t memory = 0;
{
std::shared_lock lock(this->memory_usage_mutex_);
memory = this->current_memory_usage_.load();
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/bruteforce/bruteforce.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class BruteForce : public InnerIndexInterface {
const AttributeSet& new_attrs,
const AttributeSet& origin_attrs) override;

int64_t
uint64_t
GetMemoryUsage() const override;

private:
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/hgraph/hgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ HGraph::cal_memory_usage() {
}

std::unique_lock lock(this->memory_usage_mutex_);
this->current_memory_usage_.store(static_cast<int64_t>(memory));
this->current_memory_usage_.store(memory);
}

} // namespace vsag
2 changes: 1 addition & 1 deletion src/algorithm/hgraph/hgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class HGraph : public InnerIndexInterface {
void
GetCodeByInnerId(InnerIdType inner_id, uint8_t* data) const override;

std::string
std::unordered_map<std::string, uint64_t>
GetMemoryUsageDetail() const override;

std::pair<int64_t, int64_t>
Expand Down
33 changes: 17 additions & 16 deletions src/algorithm/hgraph/hgraph_serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,28 +396,29 @@ HGraph::Deserialize(StreamReader& reader) {
}
}

std::string
std::unordered_map<std::string, uint64_t>
HGraph::GetMemoryUsageDetail() const {
JsonType memory_usage;
if (this->ignore_reorder_) {
this->use_reorder_ = false;
std::unordered_map<std::string, uint64_t> memory_usage;
memory_usage["neighbors_mutex"] = this->neighbors_mutex_->GetMemoryUsage();
memory_usage["pool"] = this->pool_->GetMemoryUsage();
memory_usage["label_table"] = this->label_table_->GetMemoryUsage();
memory_usage["basic_flatten_codes"] = this->basic_flatten_codes_->GetMemoryUsage();
memory_usage["bottom_graph"] = this->bottom_graph_->GetMemoryUsage();
uint64_t route_graph_memory = 0;
for (const auto& route_graph : this->route_graphs_) {
route_graph_memory += route_graph->GetMemoryUsage();
}
memory_usage["basic_flatten_codes"].SetUint64(this->basic_flatten_codes_->CalcSerializeSize());
memory_usage["bottom_graph"].SetUint64(this->bottom_graph_->CalcSerializeSize());
memory_usage["route_graph"] = route_graph_memory;
if (this->has_precise_reorder()) {
memory_usage["high_precise_codes"].SetUint64(
this->high_precise_codes_->CalcSerializeSize());
memory_usage["high_precise_codes"] = this->high_precise_codes_->GetMemoryUsage();
}
uint64_t route_graph_size = 0;
for (const auto& route_graph : this->route_graphs_) {
route_graph_size += route_graph->CalcSerializeSize();
}
memory_usage["route_graph"].SetUint64(route_graph_size);
if (this->extra_info_size_ > 0 && this->extra_infos_ != nullptr) {
memory_usage["extra_infos"].SetUint64(this->extra_infos_->CalcSerializeSize());
memory_usage["extra_infos"] = this->extra_infos_->GetMemoryUsage();
}
if (this->create_new_raw_vector_ && this->raw_vector_ != nullptr) {
memory_usage["raw_vector"] = this->raw_vector_->GetMemoryUsage();
}
memory_usage["__total_size__"].SetUint64(this->CalSerializeSize());
return memory_usage.Dump();
return memory_usage;
}

} // namespace vsag
14 changes: 7 additions & 7 deletions src/algorithm/inner_index_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <atomic>
#include <shared_mutex>
#include <unordered_map>
#include <vector>

#include "container_types.h"
Expand Down Expand Up @@ -253,10 +254,10 @@ class InnerIndexInterface {
virtual DetailDataPtr
GetDetailDataByName(const std::string& name, IndexDetailInfo& info) const;

[[nodiscard]] virtual int64_t
GetEstimateBuildMemory(const int64_t num_elements) const {
[[nodiscard]] virtual uint64_t
EstimateBuildMemory(uint64_t num_elements) const {
throw VsagException(ErrorType::UNSUPPORTED_INDEX_OPERATION,
"Index doesn't support GetEstimateBuildMemory");
"Index doesn't support EstimateBuildMemory");
}

virtual void
Expand All @@ -265,15 +266,14 @@ class InnerIndexInterface {
[[nodiscard]] virtual IndexType
GetIndexType() const = 0;

[[nodiscard]] virtual int64_t
[[nodiscard]] virtual uint64_t
GetMemoryUsage() const {
std::shared_lock lock(this->memory_usage_mutex_);
return this->current_memory_usage_.load();
}

[[nodiscard]] virtual std::string
[[nodiscard]] virtual std::unordered_map<std::string, uint64_t>
GetMemoryUsageDetail() const {
// TODO(deming): implement func for every types of inner index
throw VsagException(ErrorType::UNSUPPORTED_INDEX_OPERATION,
"Index doesn't support GetMemoryUsageDetail");
}
Expand Down Expand Up @@ -572,7 +572,7 @@ class InnerIndexInterface {
protected:
std::atomic<uint64_t> total_count_{0};

std::atomic<int64_t> current_memory_usage_{0};
std::atomic<uint64_t> current_memory_usage_{0};
mutable std::shared_mutex memory_usage_mutex_{};

bool has_raw_vector_{false};
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/inner_index_interface_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ TEST_CASE("InnerIndexInterface NOT Implemented", "[ut][InnerIndexInterface]") {
REQUIRE_THROWS(empty_index->Remove(0));
REQUIRE_THROWS(empty_index->GetNumberRemoved());
REQUIRE_THROWS(empty_index->EstimateMemory(1000));
REQUIRE_THROWS(empty_index->GetEstimateBuildMemory(1000));
REQUIRE_THROWS(empty_index->EstimateBuildMemory(1000));
REQUIRE_THROWS(empty_index->Feedback(nullptr, 10, ""));
REQUIRE_THROWS(empty_index->GetStats());
REQUIRE_THROWS(empty_index->UpdateId(0, 1));
Expand Down
6 changes: 3 additions & 3 deletions src/algorithm/ivf/ivf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1307,12 +1307,12 @@ IVF::cal_memory_usage() {
memory += location_map_.size() * sizeof(uint64_t);
memory += partition_strategy_->GetMemoryUsage();
std::unique_lock lock(this->memory_usage_mutex_);
this->current_memory_usage_.store(static_cast<int64_t>(memory));
this->current_memory_usage_.store(memory);
}

int64_t
uint64_t
IVF::GetMemoryUsage() const {
int64_t memory = 0;
uint64_t memory = 0;
{
std::shared_lock lock(this->memory_usage_mutex_);
memory = this->current_memory_usage_.load();
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/ivf/ivf.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class IVF : public InnerIndexInterface {
const AttributeSet& new_attrs,
const AttributeSet& origin_attrs) override;

[[nodiscard]] int64_t
[[nodiscard]] uint64_t
GetMemoryUsage() const override;

private:
Expand Down
6 changes: 3 additions & 3 deletions src/algorithm/ivf/ivf_nearest_partition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ IVFNearestPartition::GetCentroid(BucketIdType bucket_id, Vector<float>& centroid
this->route_index_ptr_->GetCodeByInnerId(bucket_id, (uint8_t*)centroid.data());
}

[[nodiscard]] int64_t
[[nodiscard]] uint64_t
IVFNearestPartition::GetMemoryUsage() const {
return static_cast<int64_t>(sizeof(IVFNearestPartition) +
this->route_index_ptr_->GetMemoryUsage());
return static_cast<uint64_t>(sizeof(IVFNearestPartition) +
this->route_index_ptr_->GetMemoryUsage());
}
} // namespace vsag
2 changes: 1 addition & 1 deletion src/algorithm/ivf/ivf_nearest_partition.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class IVFNearestPartition : public IVFPartitionStrategy {
void
Deserialize(lvalue_or_rvalue<StreamReader> reader) override;

[[nodiscard]] int64_t
[[nodiscard]] uint64_t
GetMemoryUsage() const override;

public:
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/ivf/ivf_partition_strategy.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class IVFPartitionStrategy {
GetResidual(
uint64_t n, const float* x, float* residuals, float* centroids, BucketIdType* assign);

virtual int64_t
virtual uint64_t
GetMemoryUsage() const {
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/lazy_hgraph/lazy_hgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ LazyHGraph::GetExtraInfoByIds(const int64_t* ids, int64_t count, char* extra_inf
ActiveIndex()->GetExtraInfoByIds(ids, count, extra_infos);
}

int64_t
uint64_t
LazyHGraph::GetMemoryUsage() const {
std::shared_lock lock(this->phase_mutex_);
return ActiveIndex()->GetMemoryUsage();
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/lazy_hgraph/lazy_hgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class LazyHGraph : public InnerIndexInterface {
void
GetExtraInfoByIds(const int64_t* ids, int64_t count, char* extra_infos) const override;

[[nodiscard]] int64_t
[[nodiscard]] uint64_t
GetMemoryUsage() const override;

void
Expand Down
Loading
Loading