From 84b5f03f5e20eae2f9db47aea7a5e4c38a92d57d Mon Sep 17 00:00:00 2001 From: Jalin Wang Date: Thu, 8 Jan 2026 16:31:14 +0800 Subject: [PATCH] refactor: clarify HNSW 'm' as max neighbors of upper layer (#12) * refactor: clarify HNSW 'm' as max neighbors of upper layer - Distinguish l0/upper_max_neighbor_cnt - Define l0_max_neighbor_cnt as m * l0_multiplier. * fix ut --- src/core/algorithm/hnsw/hnsw_builder.cc | 50 +++---- src/core/algorithm/hnsw/hnsw_builder.h | 4 +- src/core/algorithm/hnsw/hnsw_builder_entity.h | 2 +- src/core/algorithm/hnsw/hnsw_context.cc | 6 +- src/core/algorithm/hnsw/hnsw_entity.cc | 5 +- src/core/algorithm/hnsw/hnsw_entity.h | 38 +++--- src/core/algorithm/hnsw/hnsw_params.h | 32 ++--- .../algorithm/hnsw/hnsw_searcher_entity.cc | 8 +- .../algorithm/hnsw/hnsw_searcher_entity.h | 4 +- src/core/algorithm/hnsw/hnsw_streamer.cc | 57 ++++---- src/core/algorithm/hnsw/hnsw_streamer.h | 4 +- .../algorithm/hnsw/hnsw_streamer_entity.cc | 10 +- .../algorithm/hnsw/hnsw_streamer_entity.h | 16 +-- .../hnsw_sparse/hnsw_sparse_builder.cc | 59 +++++---- .../hnsw_sparse/hnsw_sparse_builder.h | 5 +- .../hnsw_sparse/hnsw_sparse_builder_entity.h | 2 +- .../hnsw_sparse/hnsw_sparse_context.cc | 6 +- .../hnsw_sparse/hnsw_sparse_entity.cc | 5 +- .../hnsw_sparse/hnsw_sparse_entity.h | 124 +++--------------- .../hnsw_sparse/hnsw_sparse_params.h | 22 ++-- .../hnsw_sparse_searcher_entity.cc | 8 +- .../hnsw_sparse/hnsw_sparse_searcher_entity.h | 4 +- .../hnsw_sparse/hnsw_sparse_streamer.cc | 53 ++++---- .../hnsw_sparse/hnsw_sparse_streamer.h | 5 +- .../hnsw_sparse_streamer_entity.cc | 8 +- .../hnsw_sparse/hnsw_sparse_streamer_entity.h | 2 +- 26 files changed, 225 insertions(+), 314 deletions(-) diff --git a/src/core/algorithm/hnsw/hnsw_builder.cc b/src/core/algorithm/hnsw/hnsw_builder.cc index 1ff0cbd..6af4e8c 100644 --- a/src/core/algorithm/hnsw/hnsw_builder.cc +++ b/src/core/algorithm/hnsw/hnsw_builder.cc @@ -37,41 +37,43 @@ int HnswBuilder::init(const IndexMeta &meta, const ailego::Params ¶ms) { size_t memory_quota = 0UL; params.get(PARAM_HNSW_BUILDER_MEMORY_QUOTA, &memory_quota); params.get(PARAM_HNSW_BUILDER_THREAD_COUNT, &thread_cnt_); - params.get(PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT, &neighbor_cnt_); params.get(PARAM_HNSW_BUILDER_MIN_NEIGHBOR_COUNT, &min_neighbor_cnt_); - float ratio = HnswEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_BUILDER_UPPER_NEIGHBOR_RATIO, &ratio); - upper_neighbor_cnt_ = ratio * neighbor_cnt_; params.get(PARAM_HNSW_BUILDER_EFCONSTRUCTION, &ef_construction_); params.get(PARAM_HNSW_BUILDER_SCALING_FACTOR, &scaling_factor_); params.get(PARAM_HNSW_BUILDER_CHECK_INTERVAL_SECS, &check_interval_secs_); - ratio = HnswEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_BUILDER_NEIGHBOR_PRUNE_RATIO, &ratio); - size_t prune_cnt = neighbor_cnt_ * ratio; + params.get(PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT, &upper_max_neighbor_cnt_); + float multiplier = HnswEntity::kDefaultL0MaxNeighborCntMultiplier; + params.get(PARAM_HNSW_BUILDER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER, &multiplier); + l0_max_neighbor_cnt_ = multiplier * upper_max_neighbor_cnt_; + + multiplier = HnswEntity::kDefaultNeighborPruneMultiplier; + params.get(PARAM_HNSW_BUILDER_NEIGHBOR_PRUNE_MULTIPLIER, &multiplier); + size_t prune_cnt = multiplier * upper_max_neighbor_cnt_; if (ef_construction_ == 0) { ef_construction_ = HnswEntity::kDefaultEfConstruction; } - if (neighbor_cnt_ == 0) { - neighbor_cnt_ = HnswEntity::kDefaultNeighborCnt; + if (upper_max_neighbor_cnt_ == 0) { + upper_max_neighbor_cnt_ = HnswEntity::kDefaultUpperMaxNeighborCnt; } - if (neighbor_cnt_ > kMaxNeighborCnt) { + if (upper_max_neighbor_cnt_ > kMaxNeighborCnt) { LOG_ERROR("[%s] must be in range (0,%d]", PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (min_neighbor_cnt_ > neighbor_cnt_) { + if (min_neighbor_cnt_ > upper_max_neighbor_cnt_) { LOG_ERROR("[%s]-[%d] must be <= [%s]-[%d]", PARAM_HNSW_BUILDER_MIN_NEIGHBOR_COUNT.c_str(), min_neighbor_cnt_, - PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), neighbor_cnt_); + PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), + upper_max_neighbor_cnt_); return IndexError_InvalidArgument; } - if (upper_neighbor_cnt_ == 0) { - neighbor_cnt_ = HnswEntity::kDefaultUpperNeighborCnt; + if (l0_max_neighbor_cnt_ == 0) { + l0_max_neighbor_cnt_ = HnswEntity::kDefaultUpperMaxNeighborCnt; } - if (upper_neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { - LOG_ERROR("UpperNeighborCnt must be in range (0,%d)", + if (l0_max_neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { + LOG_ERROR("L0MaxNeighborCnt must be in range (0,%d)", HnswEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } @@ -92,7 +94,7 @@ int HnswBuilder::init(const IndexMeta &meta, const ailego::Params ¶ms) { std::thread::hardware_concurrency()); } if (prune_cnt == 0UL) { - prune_cnt = upper_neighbor_cnt_; + prune_cnt = upper_max_neighbor_cnt_; } metric_ = IndexFactory::CreateMetric(meta_.metric_name()); @@ -109,9 +111,9 @@ int HnswBuilder::init(const IndexMeta &meta, const ailego::Params ¶ms) { entity_.set_vector_size(meta_.element_size()); entity_.set_ef_construction(ef_construction_); - entity_.set_neighbor_cnt(neighbor_cnt_); + entity_.set_l0_neighbor_cnt(l0_max_neighbor_cnt_); entity_.set_min_neighbor_cnt(min_neighbor_cnt_); - entity_.set_upper_neighbor_cnt(upper_neighbor_cnt_); + entity_.set_upper_neighbor_cnt(upper_max_neighbor_cnt_); entity_.set_scaling_factor(scaling_factor_); entity_.set_memory_quota(memory_quota); entity_.set_prune_cnt(prune_cnt); @@ -131,10 +133,10 @@ int HnswBuilder::init(const IndexMeta &meta, const ailego::Params ¶ms) { state_ = BUILD_STATE_INITED; LOG_INFO( "End HnswBuilder::init, params: vectorSize=%u efConstruction=%u " - "neighborCnt=%u upperNeighborCnt=%u scalingFactor=%u " + "l0NeighborCnt=%u upperNeighborCnt=%u scalingFactor=%u " "memoryQuota=%zu neighborPruneCnt=%zu metricName=%s ", - meta_.element_size(), ef_construction_, neighbor_cnt_, - upper_neighbor_cnt_, scaling_factor_, memory_quota, prune_cnt, + meta_.element_size(), ef_construction_, l0_max_neighbor_cnt_, + upper_max_neighbor_cnt_, scaling_factor_, memory_quota, prune_cnt, meta_.metric_name().c_str()); return 0; @@ -143,9 +145,9 @@ int HnswBuilder::init(const IndexMeta &meta, const ailego::Params ¶ms) { int HnswBuilder::cleanup(void) { LOG_INFO("Begin HnswBuilder::cleanup"); - neighbor_cnt_ = HnswEntity::kDefaultNeighborCnt; + l0_max_neighbor_cnt_ = HnswEntity::kDefaultL0MaxNeighborCnt; min_neighbor_cnt_ = 0; - upper_neighbor_cnt_ = HnswEntity::kDefaultUpperNeighborCnt; + upper_max_neighbor_cnt_ = HnswEntity::kDefaultUpperMaxNeighborCnt; ef_construction_ = HnswEntity::kDefaultEfConstruction; scaling_factor_ = HnswEntity::kDefaultScalingFactor; check_interval_secs_ = kDefaultLogIntervalSecs; diff --git a/src/core/algorithm/hnsw/hnsw_builder.h b/src/core/algorithm/hnsw/hnsw_builder.h index 7536fe4..e85534a 100644 --- a/src/core/algorithm/hnsw/hnsw_builder.h +++ b/src/core/algorithm/hnsw/hnsw_builder.h @@ -71,9 +71,9 @@ class HnswBuilder : public IndexBuilder { HnswBuilderEntity entity_{}; HnswAlgorithm::UPointer alg_; // impl graph algorithm uint32_t thread_cnt_{0}; - uint32_t neighbor_cnt_{HnswEntity::kDefaultNeighborCnt}; uint32_t min_neighbor_cnt_{0}; - uint32_t upper_neighbor_cnt_{HnswEntity::kDefaultUpperNeighborCnt}; + uint32_t upper_max_neighbor_cnt_{HnswEntity::kDefaultUpperMaxNeighborCnt}; + uint32_t l0_max_neighbor_cnt_{HnswEntity::kDefaultL0MaxNeighborCnt}; uint32_t ef_construction_{HnswEntity::kDefaultEfConstruction}; uint32_t scaling_factor_{HnswEntity::kDefaultScalingFactor}; uint32_t check_interval_secs_{kDefaultLogIntervalSecs}; diff --git a/src/core/algorithm/hnsw/hnsw_builder_entity.h b/src/core/algorithm/hnsw/hnsw_builder_entity.h index fb303df..5edfd6d 100644 --- a/src/core/algorithm/hnsw/hnsw_builder_entity.h +++ b/src/core/algorithm/hnsw/hnsw_builder_entity.h @@ -97,7 +97,7 @@ class HnswBuilderEntity : public HnswEntity { //! Get neighbors size inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } //! Get upper neighbors size diff --git a/src/core/algorithm/hnsw/hnsw_context.cc b/src/core/algorithm/hnsw/hnsw_context.cc index c832bc5..56a5081 100644 --- a/src/core/algorithm/hnsw/hnsw_context.cc +++ b/src/core/algorithm/hnsw/hnsw_context.cc @@ -47,7 +47,7 @@ int HnswContext::init(ContextType type) { return ret; } candidates_.limit(max_scan_num_); - update_heap_.limit(entity_->neighbor_cnt() + 1); + update_heap_.limit(entity_->l0_neighbor_cnt() + 1); break; case kSearcherContext: @@ -74,7 +74,7 @@ int HnswContext::init(ContextType type) { return ret; } - update_heap_.limit(entity_->neighbor_cnt() + 1); + update_heap_.limit(entity_->l0_neighbor_cnt() + 1); candidates_.limit(max_scan_num_); check_need_adjuct_ctx(); @@ -240,7 +240,7 @@ int HnswContext::update_context(ContextType type, const IndexMeta &meta, return IndexError_Runtime; } - update_heap_.limit(entity->neighbor_cnt() + 1); + update_heap_.limit(entity->l0_neighbor_cnt() + 1); candidates_.limit(max_scan_num_); topk_heap_.limit(std::max(topk_, ef_)); break; diff --git a/src/core/algorithm/hnsw/hnsw_entity.cc b/src/core/algorithm/hnsw/hnsw_entity.cc index b521db4..d5b3421 100644 --- a/src/core/algorithm/hnsw/hnsw_entity.cc +++ b/src/core/algorithm/hnsw/hnsw_entity.cc @@ -224,7 +224,7 @@ int64_t HnswEntity::dump_graph_neighbors( graph_meta.reserve(doc_cnt()); size_t offset = 0; uint32_t crc = 0; - node_id_t mapping[neighbor_cnt()]; + node_id_t mapping[l0_neighbor_cnt()]; uint32_t min_neighbor_count = 10000; uint32_t max_neighbor_count = 0; @@ -234,7 +234,8 @@ int64_t HnswEntity::dump_graph_neighbors( const Neighbors neighbors = get_neighbors(0, reorder_mapping.empty() ? id : reorder_mapping[id]); ailego_assert_with(!!neighbors.data, "invalid neighbors"); - ailego_assert_with(neighbors.size() <= neighbor_cnt(), "invalid neighbors"); + ailego_assert_with(neighbors.size() <= l0_neighbor_cnt(), + "invalid neighbors"); uint32_t neighbor_count = neighbors.size(); if (neighbor_count < min_neighbor_count) { diff --git a/src/core/algorithm/hnsw/hnsw_entity.h b/src/core/algorithm/hnsw/hnsw_entity.h index abbb805..cc4c92e 100644 --- a/src/core/algorithm/hnsw/hnsw_entity.h +++ b/src/core/algorithm/hnsw/hnsw_entity.h @@ -42,7 +42,7 @@ struct GraphHeader { uint32_t doc_count; uint32_t vector_size; uint32_t node_size; - uint32_t max_neighbor_count; + uint32_t l0_neighbor_count; uint32_t prune_type; uint32_t prune_neighbor_count; uint32_t ef_construction; @@ -58,7 +58,7 @@ static_assert(sizeof(GraphHeader) % 32 == 0, struct HnswHeader { uint32_t size; // header size uint32_t revision; // current total docs of the graph - uint32_t thumb_neighbor_count; + uint32_t upper_neighbor_count; uint32_t ef_construction; uint32_t scaling_factor; uint32_t max_level; @@ -100,12 +100,12 @@ struct HNSWHeader { hnsw.size = sizeof(HnswHeader); } - size_t neighbor_cnt() const { - return graph.max_neighbor_count; + size_t l0_neighbor_cnt() const { + return graph.l0_neighbor_count; } size_t upper_neighbor_cnt() const { - return hnsw.thumb_neighbor_count; + return hnsw.upper_neighbor_count; } size_t vector_size() const { @@ -201,13 +201,13 @@ class HnswEntity { //! Get max neighbor size of graph level inline size_t neighbor_cnt(level_t level) const { - return level == 0 ? header_.graph.max_neighbor_count - : header_.hnsw.thumb_neighbor_count; + return level == 0 ? header_.graph.l0_neighbor_count + : header_.hnsw.upper_neighbor_count; } //! get max neighbor size of graph level 0 - inline size_t neighbor_cnt() const { - return header_.graph.max_neighbor_count; + inline size_t l0_neighbor_cnt() const { + return header_.graph.l0_neighbor_count; } //! get min neighbor size of graph @@ -217,7 +217,7 @@ class HnswEntity { //! get upper neighbor size of graph level other than 0 inline size_t upper_neighbor_cnt() const { - return header_.hnsw.thumb_neighbor_count; + return header_.hnsw.upper_neighbor_count; } //! Get current total doc of the hnsw graph @@ -276,8 +276,8 @@ class HnswEntity { header_.hnsw.scaling_factor = val; } - void set_neighbor_cnt(size_t cnt) { - header_.graph.max_neighbor_count = cnt; + void set_l0_neighbor_cnt(size_t cnt) { + header_.graph.l0_neighbor_count = cnt; } void set_min_neighbor_cnt(size_t cnt) { @@ -285,7 +285,7 @@ class HnswEntity { } void set_upper_neighbor_cnt(size_t cnt) { - header_.hnsw.thumb_neighbor_count = cnt; + header_.hnsw.upper_neighbor_count = cnt; } void set_ef_construction(size_t ef) { @@ -499,8 +499,8 @@ class HnswEntity { constexpr static size_t kMaxGraphLayers = 15; constexpr static uint32_t kDefaultEfConstruction = 500; constexpr static uint32_t kDefaultEf = 500; - constexpr static uint32_t kDefaultNeighborCnt = 100; - constexpr static uint32_t kDefaultUpperNeighborCnt = 50; + constexpr static uint32_t kDefaultUpperMaxNeighborCnt = 50; // M of HNSW + constexpr static uint32_t kDefaultL0MaxNeighborCnt = 100; constexpr static uint32_t kMaxNeighborCnt = 65535; constexpr static float kDefaultScanRatio = 0.1f; constexpr static uint32_t kDefaultMinScanLimit = 10000; @@ -514,10 +514,10 @@ class HnswEntity { constexpr static size_t kMaxChunkSize = 0xFFFFFFFF; constexpr static size_t kDefaultChunkSize = 2UL * 1024UL * 1024UL; constexpr static size_t kDefaultMaxChunkCnt = 50000UL; - constexpr static float kDefaultNeighborPruneRatio = - 0.5f; // prune count / neighbor count - constexpr static float kDefaultUpperNeighborRatio = - 0.5f; // upper neighbor count / neighbor count + constexpr static float kDefaultNeighborPruneMultiplier = + 1.0f; // prune_cnt = upper_max_neighbor_cnt * multiplier + constexpr static float kDefaultL0MaxNeighborCntMultiplier = + 2.0f; // l0_max_neighbor_cnt = upper_max_neighbor_cnt * multiplier protected: HNSWHeader header_{}; diff --git a/src/core/algorithm/hnsw/hnsw_params.h b/src/core/algorithm/hnsw/hnsw_params.h index 7a4aaad..a7b7c92 100644 --- a/src/core/algorithm/hnsw/hnsw_params.h +++ b/src/core/algorithm/hnsw/hnsw_params.h @@ -28,20 +28,14 @@ static const std::string PARAM_HNSW_BUILDER_SCALING_FACTOR( "proxima.hnsw.builder.scaling_factor"); static const std::string PARAM_HNSW_BUILDER_CHECK_INTERVAL_SECS( "proxima.hnsw.builder.check_interval_secs"); -static const std::string PARAM_HNSW_BUILDER_NEIGHBOR_PRUNE_RATIO( - "proxima.hnsw.builder.neighbor_prune_ratio"); -static const std::string PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT( - "proxima.hnsw.builder.max_neighbor_count"); +static const std::string PARAM_HNSW_BUILDER_NEIGHBOR_PRUNE_MULTIPLIER( + "proxima.hnsw.builder.neighbor_prune_multiplier"); static const std::string PARAM_HNSW_BUILDER_MIN_NEIGHBOR_COUNT( "proxima.hnsw.builder.min_neighbor_count"); -static const std::string PARAM_HNSW_BUILDER_UPPER_NEIGHBOR_RATIO( - "proxima.hnsw.builder.upper_neighbor_ratio"); -static const std::string PARAM_HNSW_BUILDER_HYBRID_VECTOR_ENABLE( - "proxima.hnsw.builder.hybrid_vector_enable"); -static const std::string PARAM_HNSW_BUILDER_HYBRID_VECTOR_SEPARATE_NEIGHBOR( - "proxima.hnsw.builder.hybrid_vector_separate_neighbor"); -static const std::string PARAM_HNSW_BUILDER_SPARSE_NEIGHBOR_RATIO( - "proxima.hnsw.builder.sparse_neighbor_ratio"); +static const std::string PARAM_HNSW_BUILDER_MAX_NEIGHBOR_COUNT( + "proxima.hnsw.builder.max_neighbor_count"); +static const std::string PARAM_HNSW_BUILDER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER( + "proxima.hnsw.builder.l0_max_neighbor_count_multiplier"); static const std::string PARAM_HNSW_SEARCHER_EF("proxima.hnsw.searcher.ef"); static const std::string PARAM_HNSW_SEARCHER_BRUTE_FORCE_THRESHOLD( @@ -70,8 +64,8 @@ static const std::string PARAM_HNSW_STREAMER_EFCONSTRUCTION( "proxima.hnsw.streamer.efconstruction"); static const std::string PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT( "proxima.hnsw.streamer.max_neighbor_count"); -static const std::string PARAM_HNSW_STREAMER_UPPER_NEIGHBOR_RATIO( - "proxima.hnsw.streamer.upper_neighbor_ratio"); +static const std::string PARAM_HNSW_STREAMER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER( + "proxima.hnsw.streamer.l0_max_neighbor_count_multiplier"); static const std::string PARAM_HNSW_STREAMER_SCALING_FACTOR( "proxima.hnsw.streamer.scaling_factor"); static const std::string PARAM_HNSW_STREAMER_BRUTE_FORCE_THRESHOLD( @@ -88,8 +82,8 @@ static const std::string PARAM_HNSW_STREAMER_VISIT_BLOOMFILTER_NEGATIVE_PROB( "proxima.hnsw.streamer.visit_bloomfilter_negative_prob"); static const std::string PARAM_HNSW_STREAMER_CHECK_CRC_ENABLE( "proxima.hnsw.streamer.check_crc_enable"); -static const std::string PARAM_HNSW_STREAMER_NEIGHBOR_PRUNE_RATIO( - "proxima.hnsw.streamer.neighbor_prune_ratio"); +static const std::string PARAM_HNSW_STREAMER_NEIGHBOR_PRUNE_MULTIPLIER( + "proxima.hnsw.streamer.neighbor_prune_multiplier"); static const std::string PARAM_HNSW_STREAMER_CHUNK_SIZE( "proxima.hnsw.streamer.chunk_size"); static const std::string PARAM_HNSW_STREAMER_FILTER_SAME_KEY( @@ -100,12 +94,6 @@ static const std::string PARAM_HNSW_STREAMER_MIN_NEIGHBOR_COUNT( "proxima.hnsw.streamer.min_neighbor_count"); static const std::string PARAM_HNSW_STREAMER_FORCE_PADDING_RESULT_ENABLE( "proxima.hnsw.streamer.force_padding_result_enable"); -static const std::string PARAM_HNSW_STREAMER_HYBRID_VECTOR_ENABLE( - "proxima.hnsw.streamer.hybrid_vector_enable"); -static const std::string PARAM_HNSW_STREAMER_HYBRID_VECTOR_SEPARATE_NEIGHBOR( - "proxima.hnsw.streamer.hybrid_vector_separate_neighbor"); -static const std::string PARAM_HNSW_STREAMER_SPARSE_NEIGHBOR_RATIO( - "proxima.hnsw.streamer.sparse_neighbor_ratio"); static const std::string PARAM_HNSW_STREAMER_ESTIMATE_DOC_COUNT( "proxima.hnsw.streamer.estimate_doc_count"); static const std::string PARAM_HNSW_STREAMER_USE_ID_MAP( diff --git a/src/core/algorithm/hnsw/hnsw_searcher_entity.cc b/src/core/algorithm/hnsw/hnsw_searcher_entity.cc index ca092f2..5b9c8c5 100644 --- a/src/core/algorithm/hnsw/hnsw_searcher_entity.cc +++ b/src/core/algorithm/hnsw/hnsw_searcher_entity.cc @@ -223,11 +223,11 @@ int HnswSearcherEntity::load(const IndexStorage::Pointer &container, LOG_INFO( "Index info: docCnt=%u entryPoint=%u maxLevel=%d efConstruct=%zu " - "neighborCnt=%zu upperNeighborCnt=%zu scalingFactor=%zu " + "l0NeighborCnt=%zu upperNeighborCnt=%zu scalingFactor=%zu " "vectorSize=%zu nodeSize=%zu vectorSegmentSize=%zu keySegmentSize=%zu " "neighborsSegmentSize=%zu neighborsMetaSegmentSize=%zu ", doc_cnt(), entry_point(), cur_max_level(), ef_construction(), - neighbor_cnt(), upper_neighbor_cnt(), scaling_factor(), vector_size(), + l0_neighbor_cnt(), upper_neighbor_cnt(), scaling_factor(), vector_size(), node_size(), vectors_->data_size(), keys_->data_size(), neighbors_ == nullptr ? 0 : neighbors_->data_size(), neighbors_meta_ == nullptr ? 0 : neighbors_meta_->data_size()); @@ -263,7 +263,7 @@ int HnswSearcherEntity::load_segments(bool check_crc) { } memcpy(&hd.hnsw, data, sizeof(hd.hnsw)); *mutable_header() = hd; - segment_datas_.resize(std::max(neighbor_cnt(), upper_neighbor_cnt())); + segment_datas_.resize(std::max(l0_neighbor_cnt(), upper_neighbor_cnt())); vectors_ = storage_->get(kGraphFeaturesSegmentId); if (!vectors_) { @@ -403,7 +403,7 @@ int HnswSearcherEntity::get_fixed_neighbors( return IndexError_InvalidArgument; } - size_t fixed_neighbor_cnt = neighbor_cnt(); + size_t fixed_neighbor_cnt = l0_neighbor_cnt(); fixed_neighbors->resize((fixed_neighbor_cnt + 1) * doc_cnt(), kInvalidNodeId); size_t neighbors_cnt_offset = fixed_neighbor_cnt * doc_cnt(); diff --git a/src/core/algorithm/hnsw/hnsw_searcher_entity.h b/src/core/algorithm/hnsw/hnsw_searcher_entity.h index dc84ccc..3420097 100644 --- a/src/core/algorithm/hnsw/hnsw_searcher_entity.h +++ b/src/core/algorithm/hnsw/hnsw_searcher_entity.h @@ -112,7 +112,7 @@ class HnswSearcherEntity : public HnswEntity { upper_neighbors_(neighbor_group.upper_neighbors), upper_neighbors_meta_(neighbor_group.upper_neighbors_meta), neighbors_in_memory_enabled_(neighbors_in_memory_enabled) { - segment_datas_.resize(std::max(neighbor_cnt(), upper_neighbor_cnt()), + segment_datas_.resize(std::max(l0_neighbor_cnt(), upper_neighbor_cnt()), IndexStorage::SegmentData(0U, 0U)); fixed_neighbors_ = fixed_neighbors; } @@ -120,7 +120,7 @@ class HnswSearcherEntity : public HnswEntity { bool do_crc_check(std::vector &segments) const; inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } inline size_t upper_neighbors_size() const { diff --git a/src/core/algorithm/hnsw/hnsw_streamer.cc b/src/core/algorithm/hnsw/hnsw_streamer.cc index b14ad05..397d890 100644 --- a/src/core/algorithm/hnsw/hnsw_streamer.cc +++ b/src/core/algorithm/hnsw/hnsw_streamer.cc @@ -38,10 +38,15 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { meta_.set_streamer("HnswStreamer", HnswEntity::kRevision, params); params.get(PARAM_HNSW_STREAMER_MAX_INDEX_SIZE, &max_index_size_); - params.get(PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT, &neighbor_cnt_); - float ratio = HnswEntity::kDefaultUpperNeighborRatio; - params.get(PARAM_HNSW_STREAMER_UPPER_NEIGHBOR_RATIO, &ratio); - upper_neighbor_cnt_ = ratio * neighbor_cnt_; + + params.get(PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT, &upper_max_neighbor_cnt_); + float multiplier = HnswEntity::kDefaultL0MaxNeighborCntMultiplier; + params.get(PARAM_HNSW_STREAMER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER, &multiplier); + l0_max_neighbor_cnt_ = multiplier * upper_max_neighbor_cnt_; + + multiplier = HnswEntity::kDefaultNeighborPruneMultiplier; + params.get(PARAM_HNSW_STREAMER_NEIGHBOR_PRUNE_MULTIPLIER, &multiplier); + size_t prune_cnt = multiplier * upper_max_neighbor_cnt_; params.get(PARAM_HNSW_STREAMER_DOCS_HARD_LIMIT, &docs_hard_limit_); params.get(PARAM_HNSW_STREAMER_EF, &ef_); @@ -55,9 +60,6 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { params.get(PARAM_HNSW_STREAMER_MAX_SCAN_LIMIT, &max_scan_limit_); params.get(PARAM_HNSW_STREAMER_MIN_SCAN_LIMIT, &min_scan_limit_); params.get(PARAM_HNSW_STREAMER_CHECK_CRC_ENABLE, &check_crc_enabled_); - ratio = HnswEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_STREAMER_NEIGHBOR_PRUNE_RATIO, &ratio); - size_t prune_cnt = ratio * neighbor_cnt_; params.get(PARAM_HNSW_STREAMER_CHUNK_SIZE, &chunk_size_); params.get(PARAM_HNSW_STREAMER_FILTER_SAME_KEY, &filter_same_key_); params.get(PARAM_HNSW_STREAMER_GET_VECTOR_ENABLE, &get_vector_enabled_); @@ -84,27 +86,28 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { if (ef_construction_ == 0U) { ef_construction_ = HnswEntity::kDefaultEfConstruction; } - if (neighbor_cnt_ == 0U) { - neighbor_cnt_ = HnswEntity::kDefaultNeighborCnt; + if (upper_max_neighbor_cnt_ == 0U) { + upper_max_neighbor_cnt_ = HnswEntity::kDefaultUpperMaxNeighborCnt; } - if (neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { + if (upper_max_neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { LOG_ERROR("[%s] must be in range (0,%d)", PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT.c_str(), HnswEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (upper_neighbor_cnt_ == 0U) { - upper_neighbor_cnt_ = HnswEntity::kDefaultUpperNeighborCnt; + if (l0_max_neighbor_cnt_ == 0U) { + l0_max_neighbor_cnt_ = HnswEntity::kDefaultL0MaxNeighborCnt; } - if (upper_neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { - LOG_ERROR("UpperNeighborCnt must be in range (0,%d)", + if (l0_max_neighbor_cnt_ > HnswEntity::kMaxNeighborCnt) { + LOG_ERROR("MaxL0NeighborCnt must be in range (0,%d)", HnswEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (min_neighbor_cnt_ > neighbor_cnt_) { + if (min_neighbor_cnt_ > upper_max_neighbor_cnt_) { LOG_ERROR("[%s]-[%u] must be <= [%s]-[%u]", PARAM_HNSW_STREAMER_MIN_NEIGHBOR_COUNT.c_str(), min_neighbor_cnt_, - PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT.c_str(), neighbor_cnt_); + PARAM_HNSW_STREAMER_MAX_NEIGHBOR_COUNT.c_str(), + upper_max_neighbor_cnt_); return IndexError_InvalidArgument; } @@ -137,7 +140,7 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { } if (prune_cnt == 0UL) { - prune_cnt = upper_neighbor_cnt_; + prune_cnt = upper_max_neighbor_cnt_; } if (chunk_size_ == 0UL) { chunk_size_ = HnswEntity::kDefaultChunkSize; @@ -149,8 +152,8 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { } entity_.set_ef_construction(ef_construction_); - entity_.set_neighbor_cnt(neighbor_cnt_); - entity_.set_upper_neighbor_cnt(upper_neighbor_cnt_); + entity_.set_upper_neighbor_cnt(upper_max_neighbor_cnt_); + entity_.set_l0_neighbor_cnt(l0_max_neighbor_cnt_); entity_.set_scaling_factor(scaling_factor_); entity_.set_prune_cnt(prune_cnt); @@ -169,18 +172,18 @@ int HnswStreamer::init(const IndexMeta &imeta, const ailego::Params ¶ms) { LOG_DEBUG( "Init params: maxIndexSize=%zu docsHardLimit=%zu docsSoftLimit=%zu " - "efConstruction=%u ef=%u neighborCnt=%u upperNeighborCnt=%u " + "efConstruction=%u ef=%u upperMaxNeighborCnt=%u l0MaxNeighborCnt=%u " "scalingFactor=%u maxScanRatio=%.3f minScanLimit=%zu maxScanLimit=%zu " "bfEnabled=%d bruteFoceThreshold=%zu bfNegativeProbility=%.5f " "checkCrcEnabled=%d pruneSize=%zu vectorSize=%u chunkSize=%zu " "filterSameKey=%u getVectorEnabled=%u minNeighborCount=%u " "forcePadding=%u ", max_index_size_, docs_hard_limit_, docs_soft_limit_, ef_construction_, - ef_, neighbor_cnt_, upper_neighbor_cnt_, scaling_factor_, max_scan_ratio_, - min_scan_limit_, max_scan_limit_, bf_enabled_, bruteforce_threshold_, - bf_negative_prob_, check_crc_enabled_, prune_cnt, meta_.element_size(), - chunk_size_, filter_same_key_, get_vector_enabled_, min_neighbor_cnt_, - force_padding_topk_enabled_); + ef_, upper_max_neighbor_cnt_, l0_max_neighbor_cnt_, scaling_factor_, + max_scan_ratio_, min_scan_limit_, max_scan_limit_, bf_enabled_, + bruteforce_threshold_, bf_negative_prob_, check_crc_enabled_, prune_cnt, + meta_.element_size(), chunk_size_, filter_same_key_, get_vector_enabled_, + min_neighbor_cnt_, force_padding_topk_enabled_); alg_ = HnswAlgorithm::UPointer(new HnswAlgorithm(entity_)); @@ -213,8 +216,8 @@ int HnswStreamer::cleanup(void) { max_index_size_ = 0UL; docs_hard_limit_ = HnswEntity::kDefaultDocsHardLimit; docs_soft_limit_ = 0UL; - neighbor_cnt_ = HnswEntity::kDefaultNeighborCnt; - upper_neighbor_cnt_ = HnswEntity::kDefaultUpperNeighborCnt; + upper_max_neighbor_cnt_ = HnswEntity::kDefaultUpperMaxNeighborCnt; + l0_max_neighbor_cnt_ = HnswEntity::kDefaultL0MaxNeighborCnt; ef_ = HnswEntity::kDefaultEf; ef_construction_ = HnswEntity::kDefaultEfConstruction; bf_enabled_ = false; diff --git a/src/core/algorithm/hnsw/hnsw_streamer.h b/src/core/algorithm/hnsw/hnsw_streamer.h index e81c6f2..a2162cb 100644 --- a/src/core/algorithm/hnsw/hnsw_streamer.h +++ b/src/core/algorithm/hnsw/hnsw_streamer.h @@ -199,9 +199,9 @@ class HnswStreamer : public IndexStreamer { size_t chunk_size_{HnswEntity::kDefaultChunkSize}; size_t docs_hard_limit_{HnswEntity::kDefaultDocsHardLimit}; size_t docs_soft_limit_{0UL}; - uint32_t neighbor_cnt_{HnswEntity::kDefaultNeighborCnt}; uint32_t min_neighbor_cnt_{0u}; - uint32_t upper_neighbor_cnt_{HnswEntity::kDefaultUpperNeighborCnt}; + uint32_t upper_max_neighbor_cnt_{HnswEntity::kDefaultUpperMaxNeighborCnt}; + uint32_t l0_max_neighbor_cnt_{HnswEntity::kDefaultL0MaxNeighborCnt}; uint32_t ef_{HnswEntity::kDefaultEf}; uint32_t ef_construction_{HnswEntity::kDefaultEfConstruction}; uint32_t scaling_factor_{HnswEntity::kDefaultScalingFactor}; diff --git a/src/core/algorithm/hnsw/hnsw_streamer_entity.cc b/src/core/algorithm/hnsw/hnsw_streamer_entity.cc index 510087f..feafa57 100644 --- a/src/core/algorithm/hnsw/hnsw_streamer_entity.cc +++ b/src/core/algorithm/hnsw/hnsw_streamer_entity.cc @@ -359,9 +359,9 @@ int HnswStreamerEntity::open(IndexStorage::Pointer stg, uint64_t max_index_size, } LOG_INFO( - "Open index, neighborCnt=%zu upperneighborCnt=%zu " + "Open index, l0NeighborCnt=%zu upperNeighborCnt=%zu " "efConstruction=%zu curDocCnt=%u totalVecs=%u maxLevel=%u", - neighbor_cnt(), upper_neighbor_cnt(), ef_construction(), doc_cnt(), + l0_neighbor_cnt(), upper_neighbor_cnt(), ef_construction(), doc_cnt(), total_vecs, cur_max_level()); //! try to correct the docCnt if index not fully flushed if (doc_cnt() != total_vecs) { @@ -440,10 +440,10 @@ int HnswStreamerEntity::dump(const IndexDumper::Pointer &dumper) { } int HnswStreamerEntity::check_hnsw_index(const HNSWHeader *hd) const { - if (neighbor_cnt() != hd->neighbor_cnt() || + if (l0_neighbor_cnt() != hd->l0_neighbor_cnt() || upper_neighbor_cnt() != hd->upper_neighbor_cnt()) { - LOG_ERROR("Param neighbors:%zu:%zu mismatch index previous %zu:%zu", - neighbor_cnt(), upper_neighbor_cnt(), hd->neighbor_cnt(), + LOG_ERROR("Param neighbor cnt: %zu:%zu mismatch index previous %zu:%zu", + l0_neighbor_cnt(), upper_neighbor_cnt(), hd->l0_neighbor_cnt(), hd->upper_neighbor_cnt()); return IndexError_Mismatch; } diff --git a/src/core/algorithm/hnsw/hnsw_streamer_entity.h b/src/core/algorithm/hnsw/hnsw_streamer_entity.h index cd515ce..7031b56 100644 --- a/src/core/algorithm/hnsw/hnsw_streamer_entity.h +++ b/src/core/algorithm/hnsw/hnsw_streamer_entity.h @@ -178,26 +178,16 @@ class HnswStreamerEntity : public HnswEntity { std::cout << "key map ends" << std::endl; } - //! Get neighbors size + //! Get l0 neighbors size inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } - //! Get neighbors size in hybrid mode - inline size_t neighbors_size_hybrid() const { - return sizeof(NeighborsHeader) * 2 + neighbor_cnt() * sizeof(node_id_t); - } - - //! Get upper neighbors size + //! Get neighbors size for level > 0 inline size_t upper_neighbors_size() const { return sizeof(NeighborsHeader) + upper_neighbor_cnt() * sizeof(node_id_t); } - //! Get upper neighbors size in hybrid mode - inline size_t upper_neighbors_size_hybrid() const { - return 2 * - (sizeof(NeighborsHeader) + upper_neighbor_cnt() * sizeof(node_id_t)); - } private: union UpperNeighborIndexMeta { diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.cc index 4516e41..3230ff0 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.cc @@ -38,44 +38,47 @@ int HnswSparseBuilder::init(const IndexMeta &meta, size_t memory_quota = 0UL; params.get(PARAM_HNSW_SPARSE_BUILDER_MEMORY_QUOTA, &memory_quota); params.get(PARAM_HNSW_SPARSE_BUILDER_THREAD_COUNT, &thread_cnt_); - params.get(PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT, &neighbor_cnt_); - params.get(PARAM_HNSW_SPARSE_BUILDER_MIN_NEIGHBOR_COUNT, &min_neighbor_cnt_); - float ratio = HnswSparseEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_SPARSE_BUILDER_UPPER_NEIGHBOR_RATIO, &ratio); - upper_neighbor_cnt_ = ratio * neighbor_cnt_; params.get(PARAM_HNSW_SPARSE_BUILDER_EFCONSTRUCTION, &ef_construction_); params.get(PARAM_HNSW_SPARSE_BUILDER_SCALING_FACTOR, &scaling_factor_); params.get(PARAM_HNSW_SPARSE_BUILDER_CHECK_INTERVAL_SECS, &check_interval_secs_); - ratio = HnswSparseEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_SPARSE_BUILDER_NEIGHBOR_PRUNE_RATIO, &ratio); - size_t prune_cnt = neighbor_cnt_ * ratio; + params.get(PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT, + &upper_max_neighbor_cnt_); + float multiplier = HnswSparseEntity::kDefaultL0MaxNeighborCntMultiplier; + params.get(PARAM_HNSW_SPARSE_BUILDER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER, + &multiplier); + l0_max_neighbor_cnt_ = multiplier * upper_max_neighbor_cnt_; + + multiplier = HnswSparseEntity::kDefaultNeighborPruneMultiplier; + params.get(PARAM_HNSW_SPARSE_BUILDER_NEIGHBOR_PRUNE_MULTIPLIER, &multiplier); + size_t prune_cnt = multiplier * upper_max_neighbor_cnt_; if (ef_construction_ == 0) { ef_construction_ = HnswSparseEntity::kDefaultEfConstruction; } - if (neighbor_cnt_ == 0) { - neighbor_cnt_ = HnswSparseEntity::kDefaultNeighborCnt; + if (upper_max_neighbor_cnt_ == 0) { + upper_max_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperMaxNeighborCnt; } - if (neighbor_cnt_ > kMaxNeighborCnt) { + if (upper_max_neighbor_cnt_ > kMaxNeighborCnt) { LOG_ERROR("[%s] must be in range (0,%d]", PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (min_neighbor_cnt_ > neighbor_cnt_) { - LOG_ERROR( - "[%s]-[%d] must be <= [%s]-[%d]", - PARAM_HNSW_SPARSE_BUILDER_MIN_NEIGHBOR_COUNT.c_str(), min_neighbor_cnt_, - PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), neighbor_cnt_); + if (min_neighbor_cnt_ > upper_max_neighbor_cnt_) { + LOG_ERROR("[%s]-[%d] must be <= [%s]-[%d]", + PARAM_HNSW_SPARSE_BUILDER_MIN_NEIGHBOR_COUNT.c_str(), + min_neighbor_cnt_, + PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT.c_str(), + upper_max_neighbor_cnt_); return IndexError_InvalidArgument; } - if (upper_neighbor_cnt_ == 0) { - neighbor_cnt_ = HnswSparseEntity::kDefaultUpperNeighborCnt; + if (l0_max_neighbor_cnt_ == 0) { + l0_max_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperMaxNeighborCnt; } - if (upper_neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { - LOG_ERROR("UpperNeighborCnt must be in range (0,%d)", + if (l0_max_neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { + LOG_ERROR("L0MaxNeighborCnt must be in range (0,%d)", HnswSparseEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } @@ -96,7 +99,7 @@ int HnswSparseBuilder::init(const IndexMeta &meta, std::thread::hardware_concurrency()); } if (prune_cnt == 0UL) { - prune_cnt = upper_neighbor_cnt_; + prune_cnt = upper_max_neighbor_cnt_; } metric_ = IndexFactory::CreateMetric(meta_.metric_name()); @@ -111,9 +114,9 @@ int HnswSparseBuilder::init(const IndexMeta &meta, } entity_.set_ef_construction(ef_construction_); - entity_.set_neighbor_cnt(neighbor_cnt_); + entity_.set_l0_neighbor_cnt(l0_max_neighbor_cnt_); entity_.set_min_neighbor_cnt(min_neighbor_cnt_); - entity_.set_upper_neighbor_cnt(upper_neighbor_cnt_); + entity_.set_upper_neighbor_cnt(upper_max_neighbor_cnt_); entity_.set_scaling_factor(scaling_factor_); entity_.set_memory_quota(memory_quota); entity_.set_prune_cnt(prune_cnt); @@ -136,10 +139,10 @@ int HnswSparseBuilder::init(const IndexMeta &meta, state_ = BUILD_STATE_INITED; LOG_INFO( "End HnswSparseBuilder::init, params: efConstruction=%u " - "neighborCnt=%u upperNeighborCnt=%u scalingFactor=%u " + "l0NeighborCnt=%u upperNeighborCnt=%u scalingFactor=%u " "memoryQuota=%zu neighborPruneCnt=%zu measureName=%s ", - ef_construction_, neighbor_cnt_, upper_neighbor_cnt_, scaling_factor_, - memory_quota, prune_cnt, meta_.metric_name().c_str()); + ef_construction_, l0_max_neighbor_cnt_, upper_max_neighbor_cnt_, + scaling_factor_, memory_quota, prune_cnt, meta_.metric_name().c_str()); return 0; } @@ -147,9 +150,9 @@ int HnswSparseBuilder::init(const IndexMeta &meta, int HnswSparseBuilder::cleanup(void) { LOG_INFO("Begin HnswSparseBuilder::cleanup"); - neighbor_cnt_ = HnswSparseEntity::kDefaultNeighborCnt; + l0_max_neighbor_cnt_ = HnswSparseEntity::kDefaultL0MaxNeighborCnt; min_neighbor_cnt_ = 0; - upper_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperNeighborCnt; + upper_max_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperMaxNeighborCnt; ef_construction_ = HnswSparseEntity::kDefaultEfConstruction; scaling_factor_ = HnswSparseEntity::kDefaultScalingFactor; check_interval_secs_ = kDefaultLogIntervalSecs; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.h index 7e3ba4a..c5080ce 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder.h @@ -80,9 +80,10 @@ class HnswSparseBuilder : public IndexBuilder { HnswSparseBuilderEntity entity_{}; HnswSparseAlgorithm::UPointer alg_; // impl graph algorithm uint32_t thread_cnt_{0}; - uint32_t neighbor_cnt_{HnswSparseEntity::kDefaultNeighborCnt}; + uint32_t l0_max_neighbor_cnt_{HnswSparseEntity::kDefaultL0MaxNeighborCnt}; uint32_t min_neighbor_cnt_{0}; - uint32_t upper_neighbor_cnt_{HnswSparseEntity::kDefaultUpperNeighborCnt}; + uint32_t upper_max_neighbor_cnt_{ + HnswSparseEntity::kDefaultUpperMaxNeighborCnt}; uint32_t ef_construction_{HnswSparseEntity::kDefaultEfConstruction}; uint32_t scaling_factor_{HnswSparseEntity::kDefaultScalingFactor}; uint32_t check_interval_secs_{kDefaultLogIntervalSecs}; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder_entity.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder_entity.h index aaf97aa..464e1dc 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder_entity.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_builder_entity.h @@ -119,7 +119,7 @@ class HnswSparseBuilderEntity : public HnswSparseEntity { //! Get neighbors size inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } //! Get upper neighbors size diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_context.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_context.cc index cad3709..7cad11f 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_context.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_context.cc @@ -41,7 +41,7 @@ int HnswSparseContext::init(ContextType type) { return ret; } candidates_.limit(max_scan_num_); - update_heap_.limit(entity_->neighbor_cnt() + 1); + update_heap_.limit(entity_->l0_neighbor_cnt() + 1); break; case kSparseSearcherContext: @@ -68,7 +68,7 @@ int HnswSparseContext::init(ContextType type) { return ret; } - update_heap_.limit(entity_->neighbor_cnt() + 1); + update_heap_.limit(entity_->l0_neighbor_cnt() + 1); candidates_.limit(max_scan_num_); check_need_adjuct_ctx(); @@ -238,7 +238,7 @@ int HnswSparseContext::update_context(ContextType type, return IndexError_Runtime; } - update_heap_.limit(entity->neighbor_cnt() + 1); + update_heap_.limit(entity->l0_neighbor_cnt() + 1); candidates_.limit(max_scan_num_); topk_heap_.limit(std::max(topk_, ef_)); break; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.cc index 798c55c..fd88720 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.cc @@ -299,7 +299,7 @@ int64_t HnswSparseEntity::dump_graph_neighbors( graph_meta.reserve(doc_cnt()); size_t offset = 0; uint32_t crc = 0; - node_id_t mapping[neighbor_cnt()]; + node_id_t mapping[l0_neighbor_cnt()]; uint32_t min_neighbor_count = 10000; uint32_t max_neighbor_count = 0; @@ -309,7 +309,8 @@ int64_t HnswSparseEntity::dump_graph_neighbors( const Neighbors neighbors = get_neighbors(0, reorder_mapping.empty() ? id : reorder_mapping[id]); ailego_assert_with(!!neighbors.data, "invalid neighbors"); - ailego_assert_with(neighbors.size() <= neighbor_cnt(), "invalid neighbors"); + ailego_assert_with(neighbors.size() <= l0_neighbor_cnt(), + "invalid neighbors"); uint32_t neighbor_count = neighbors.size(); if (neighbor_count < min_neighbor_count) { diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.h index 8885fa4..24d3681 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_entity.h @@ -43,7 +43,7 @@ struct SparseGraphHeader { uint32_t doc_count; uint32_t vector_size; uint32_t node_size; - uint32_t max_neighbor_count; + uint32_t l0_neighbor_count; uint32_t prune_type; uint32_t prune_neighbor_count; uint32_t ef_construction; @@ -51,10 +51,8 @@ struct SparseGraphHeader { uint32_t min_neighbor_count; uint32_t sparse_meta_size; uint32_t sparse_unit_size; - uint16_t sparse_max_neighbor_count; - uint16_t sparse_min_neighbor_count; uint32_t total_sparse_count; - // uint8_t reserved_[4]; + uint8_t reserved[868]; }; static_assert(sizeof(SparseGraphHeader) % 32 == 0, @@ -64,14 +62,13 @@ static_assert(sizeof(SparseGraphHeader) % 32 == 0, struct HnswSparseHeader { uint32_t size; // header size uint32_t revision; // current total docs of the graph - uint32_t thumb_neighbor_count; + uint32_t upper_neighbor_count; uint32_t ef_construction; uint32_t scaling_factor; uint32_t max_level; uint32_t entry_point; uint32_t options; - uint16_t thumb_sparse_neighbor_count; - uint8_t reserved_[30]; + uint8_t reserved[30]; }; struct SparseData { @@ -123,11 +120,11 @@ struct HNSWSparseHeader { } size_t neighbor_cnt() const { - return graph.max_neighbor_count; + return graph.l0_neighbor_count; } size_t upper_neighbor_cnt() const { - return hnsw.thumb_neighbor_count; + return hnsw.upper_neighbor_count; } size_t vector_size() const { @@ -158,18 +155,6 @@ struct HNSWSparseHeader { return graph.total_sparse_count; } - size_t sparse_neighbor_cnt() const { - return graph.sparse_max_neighbor_count; - } - - size_t sparse_min_neighbor_cnt() const { - return graph.sparse_min_neighbor_count; - } - - size_t upper_sparse_neighbor_cnt() const { - return hnsw.thumb_sparse_neighbor_count; - } - SparseGraphHeader graph; HnswSparseHeader hnsw; }; @@ -240,13 +225,13 @@ class HnswSparseEntity { //! Get max neighbor size of graph level inline size_t neighbor_cnt(level_t level) const { - return level == 0 ? header_.graph.max_neighbor_count - : header_.hnsw.thumb_neighbor_count; + return level == 0 ? header_.graph.l0_neighbor_count + : header_.hnsw.upper_neighbor_count; } //! get max neighbor size of graph level 0 - inline size_t neighbor_cnt() const { - return header_.graph.max_neighbor_count; + inline size_t l0_neighbor_cnt() const { + return header_.graph.l0_neighbor_count; } //! get min neighbor size of graph @@ -256,61 +241,7 @@ class HnswSparseEntity { //! get upper neighbor size of graph level other than 0 inline size_t upper_neighbor_cnt() const { - return header_.hnsw.thumb_neighbor_count; - } - - size_t sparse_neighbor_cnt() const { - return header_.graph.sparse_max_neighbor_count; - } - - size_t sparse_min_neighbor_cnt() const { - return header_.graph.sparse_min_neighbor_count; - } - - size_t upper_sparse_neighbor_cnt() const { - return header_.hnsw.thumb_sparse_neighbor_count; - } - - size_t dense_neighbor_cnt() const { - return header_.graph.max_neighbor_count - - header_.graph.sparse_max_neighbor_count; - } - - size_t dense_min_neighbor_cnt() const { - return header_.graph.min_neighbor_count - - header_.graph.sparse_min_neighbor_count; - } - - size_t upper_dense_neighbor_cnt() const { - return header_.hnsw.thumb_neighbor_count - - header_.hnsw.thumb_sparse_neighbor_count; - } - - size_t sparse_neighbor_offset() const { - return sizeof(NeighborsHeader) + dense_neighbor_cnt() * sizeof(node_id_t); - } - - size_t upper_sparse_neighbor_offset() const { - return sizeof(NeighborsHeader) + - upper_dense_neighbor_cnt() * sizeof(node_id_t); - } - - size_t dense_neighbor_size() const { - return sizeof(NeighborsHeader) + dense_neighbor_cnt() * sizeof(node_id_t); - } - - size_t sparse_neighbor_size() const { - return sizeof(NeighborsHeader) + sparse_neighbor_cnt() * sizeof(node_id_t); - } - - size_t upper_dense_neighbor_size() const { - return sizeof(NeighborsHeader) + - upper_dense_neighbor_cnt() * sizeof(node_id_t); - } - - size_t upper_sparse_neighbor_size() const { - return sizeof(NeighborsHeader) + - upper_sparse_neighbor_cnt() * sizeof(node_id_t); + return header_.hnsw.upper_neighbor_count; } //! Get current total doc of the hnsw graph @@ -387,8 +318,8 @@ class HnswSparseEntity { header_.hnsw.scaling_factor = val; } - void set_neighbor_cnt(size_t cnt) { - header_.graph.max_neighbor_count = cnt; + void set_l0_neighbor_cnt(size_t cnt) { + header_.graph.l0_neighbor_count = cnt; } void set_min_neighbor_cnt(size_t cnt) { @@ -396,7 +327,7 @@ class HnswSparseEntity { } void set_upper_neighbor_cnt(size_t cnt) { - header_.hnsw.thumb_neighbor_count = cnt; + header_.hnsw.upper_neighbor_count = cnt; } void set_ef_construction(size_t ef) { @@ -411,19 +342,6 @@ class HnswSparseEntity { header_.graph.sparse_unit_size = size; } - void set_sparse_neighbor_cnt(size_t cnt) { - header_.graph.sparse_max_neighbor_count = cnt; - } - - void set_sparse_min_neighbor_cnt(size_t cnt) { - header_.graph.sparse_min_neighbor_count = cnt; - } - - - void set_upper_sparse_neighbor_cnt(size_t cnt) { - header_.hnsw.thumb_sparse_neighbor_count = cnt; - } - protected: inline const HNSWSparseHeader &header() const { return header_; @@ -670,8 +588,8 @@ class HnswSparseEntity { constexpr static size_t kMaxGraphLayers = 15; constexpr static uint32_t kDefaultEfConstruction = 500; constexpr static uint32_t kDefaultEf = 500; - constexpr static uint32_t kDefaultNeighborCnt = 100; - constexpr static uint32_t kDefaultUpperNeighborCnt = 50; + constexpr static uint32_t kDefaultUpperMaxNeighborCnt = 50; // M of HNSW + constexpr static uint32_t kDefaultL0MaxNeighborCnt = 100; constexpr static uint32_t kMaxNeighborCnt = 65535; constexpr static float kDefaultScanRatio = 0.1f; constexpr static uint32_t kDefaultMinScanLimit = 10000; @@ -685,12 +603,10 @@ class HnswSparseEntity { constexpr static size_t kMaxChunkSize = 0xFFFFFFFF; constexpr static size_t kDefaultChunkSize = 2UL * 1024UL * 1024UL; constexpr static size_t kDefaultMaxChunkCnt = 50000UL; - constexpr static float kDefaultNeighborPruneRatio = - 0.5f; // prune count / neighbor count - constexpr static float kDefaultUpperNeighborRatio = - 0.5f; // upper neighbor count / neighbor count - - constexpr static float kHybridNeighborRatio = 0.5f; + constexpr static float kDefaultNeighborPruneMultiplier = + 1.0f; // prune_cnt = upper_max_neighbor_cnt * multiplier + constexpr static float kDefaultL0MaxNeighborCntMultiplier = + 2.0f; // l0_max_neighbor_cnt = upper_max_neighbor_cnt * multiplier constexpr static uint32_t kSparseMetaSize = 2u * sizeof(uint64_t); constexpr static float kDefaultSparseNeighborRatio = 0.5f; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_params.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_params.h index b880ce9..23db15a 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_params.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_params.h @@ -28,14 +28,15 @@ static const std::string PARAM_HNSW_SPARSE_BUILDER_SCALING_FACTOR( "proxima.hnsw.sparse_builder.scaling_factor"); static const std::string PARAM_HNSW_SPARSE_BUILDER_CHECK_INTERVAL_SECS( "proxima.hnsw.sparse_builder.check_interval_secs"); -static const std::string PARAM_HNSW_SPARSE_BUILDER_NEIGHBOR_PRUNE_RATIO( - "proxima.hnsw.sparse_builder.neighbor_prune_ratio"); -static const std::string PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT( - "proxima.hnsw.sparse_builder.max_neighbor_count"); +static const std::string PARAM_HNSW_SPARSE_BUILDER_NEIGHBOR_PRUNE_MULTIPLIER( + "proxima.hnsw.sparse_builder.neighbor_prune_multiplier"); static const std::string PARAM_HNSW_SPARSE_BUILDER_MIN_NEIGHBOR_COUNT( "proxima.hnsw.sparse_builder.min_neighbor_count"); -static const std::string PARAM_HNSW_SPARSE_BUILDER_UPPER_NEIGHBOR_RATIO( - "proxima.hnsw.sparse_builder.upper_neighbor_ratio"); +static const std::string PARAM_HNSW_SPARSE_BUILDER_MAX_NEIGHBOR_COUNT( + "proxima.hnsw.sparse_builder.max_neighbor_count"); +static const std::string + PARAM_HNSW_SPARSE_BUILDER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER( + "proxima.hnsw.sparse_builder.l0_max_neighbor_count_multiplier"); static const std::string PARAM_HNSW_SPARSE_SEARCHER_EF( "proxima.hnsw.sparse_searcher.ef"); @@ -69,8 +70,9 @@ static const std::string PARAM_HNSW_SPARSE_STREAMER_EFCONSTRUCTION( "proxima.hnsw.sparse_streamer.efconstruction"); static const std::string PARAM_HNSW_SPARSE_STREAMER_MAX_NEIGHBOR_COUNT( "proxima.hnsw.sparse_streamer.max_neighbor_count"); -static const std::string PARAM_HNSW_SPARSE_STREAMER_UPPER_NEIGHBOR_RATIO( - "proxima.hnsw.sparse_streamer.upper_neighbor_ratio"); +static const std::string + PARAM_HNSW_SPARSE_STREAMER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER( + "proxima.hnsw.sparse_streamer.l0_max_neighbor_count_multiplier"); static const std::string PARAM_HNSW_SPARSE_STREAMER_SCALING_FACTOR( "proxima.hnsw.sparse_streamer.scaling_factor"); static const std::string PARAM_HNSW_SPARSE_STREAMER_BRUTE_FORCE_THRESHOLD( @@ -88,8 +90,8 @@ static const std::string "proxima.hnsw.sparse_streamer.visit_bloomfilter_negative_prob"); static const std::string PARAM_HNSW_SPARSE_STREAMER_CHECK_CRC_ENABLE( "proxima.hnsw.sparse_streamer.check_crc_enable"); -static const std::string PARAM_HNSW_SPARSE_STREAMER_NEIGHBOR_PRUNE_RATIO( - "proxima.hnsw.sparse_streamer.neighbor_prune_ratio"); +static const std::string PARAM_HNSW_SPARSE_STREAMER_NEIGHBOR_PRUNE_MULTIPLIER( + "proxima.hnsw.sparse_streamer.neighbor_prune_multiplier"); static const std::string PARAM_HNSW_SPARSE_STREAMER_CHUNK_SIZE( "proxima.hnsw.sparse_streamer.chunk_size"); static const std::string PARAM_HNSW_SPARSE_STREAMER_FILTER_SAME_KEY( diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.cc index ddb5155..fdf5092 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.cc @@ -232,12 +232,12 @@ int HnswSparseSearcherEntity::load(const IndexStorage::Pointer &container, LOG_INFO( "Index info: docCnt=%u entryPoint=%u maxLevel=%d efConstruct=%zu " - "neighborCnt=%zu upperNeighborCnt=%zu scalingFactor=%zu " + "l0NeighborCnt=%zu upperNeighborCnt=%zu scalingFactor=%zu " "nodeSize=%zu sparesMetaSegmentSize=%zu keySegmentSize=%zu " "neighborsSegmentSize=%zu neighborsMetaSegmentSize=%zu " "sparseVectorSegmentSize=%zu", doc_cnt(), entry_point(), cur_max_level(), ef_construction(), - neighbor_cnt(), upper_neighbor_cnt(), scaling_factor(), node_size(), + l0_neighbor_cnt(), upper_neighbor_cnt(), scaling_factor(), node_size(), sparse_vector_meta_->data_size(), keys_->data_size(), neighbors_->data_size(), neighbors_meta_->data_size(), sparse_vectors_->data_size()); @@ -274,7 +274,7 @@ int HnswSparseSearcherEntity::load_segments(bool check_crc) { } memcpy(&hd.hnsw, data, sizeof(hd.hnsw)); *mutable_header() = hd; - segment_datas_.resize(std::max(neighbor_cnt(), upper_neighbor_cnt())); + segment_datas_.resize(std::max(l0_neighbor_cnt(), upper_neighbor_cnt())); sparse_vector_meta_ = container_->get(kSparseGraphVectorMetaSegmentId); if (!sparse_vector_meta_) { @@ -427,7 +427,7 @@ int HnswSparseSearcherEntity::get_fixed_neighbors( return IndexError_InvalidArgument; } - size_t fixed_neighbor_cnt = neighbor_cnt(); + size_t fixed_neighbor_cnt = l0_neighbor_cnt(); fixed_neighbors->resize((fixed_neighbor_cnt + 1) * doc_cnt(), kInvalidNodeId); size_t neighbors_cnt_offset = fixed_neighbor_cnt * doc_cnt(); diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.h index 0ddeca5..2a14149 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_searcher_entity.h @@ -137,7 +137,7 @@ class HnswSparseSearcherEntity : public HnswSparseEntity { sparse_vector_meta_(sparse_vector_meta), sparse_vectors_(sparse_vectors), neighbors_in_memory_enabled_(neighbors_in_memory_enabled) { - segment_datas_.resize(std::max(neighbor_cnt(), upper_neighbor_cnt()), + segment_datas_.resize(std::max(l0_neighbor_cnt(), upper_neighbor_cnt()), IndexStorage::SegmentData(0U, 0U)); fixed_neighbors_ = fixed_neighbors; } @@ -145,7 +145,7 @@ class HnswSparseSearcherEntity : public HnswSparseEntity { bool do_crc_check(std::vector &segments) const; inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } inline size_t upper_neighbors_size() const { diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.cc index adf2c11..67885bd 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.cc @@ -38,10 +38,16 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, meta_.set_streamer("HnswSparseStreamer", HnswSparseEntity::kRevision, params); params.get(PARAM_HNSW_SPARSE_STREAMER_MAX_INDEX_SIZE, &max_index_size_); - params.get(PARAM_HNSW_SPARSE_STREAMER_MAX_NEIGHBOR_COUNT, &neighbor_cnt_); - float ratio = HnswSparseEntity::kDefaultUpperNeighborRatio; - params.get(PARAM_HNSW_SPARSE_STREAMER_UPPER_NEIGHBOR_RATIO, &ratio); - upper_neighbor_cnt_ = ratio * neighbor_cnt_; + params.get(PARAM_HNSW_SPARSE_STREAMER_MAX_NEIGHBOR_COUNT, + &upper_max_neighbor_cnt_); + float multiplier = HnswSparseEntity::kDefaultL0MaxNeighborCntMultiplier; + params.get(PARAM_HNSW_SPARSE_STREAMER_L0_MAX_NEIGHBOR_COUNT_MULTIPLIER, + &multiplier); + l0_max_neighbor_cnt_ = multiplier * upper_max_neighbor_cnt_; + + multiplier = HnswSparseEntity::kDefaultNeighborPruneMultiplier; + params.get(PARAM_HNSW_SPARSE_STREAMER_NEIGHBOR_PRUNE_MULTIPLIER, &multiplier); + size_t prune_cnt = multiplier * upper_max_neighbor_cnt_; params.get(PARAM_HNSW_SPARSE_STREAMER_DOCS_HARD_LIMIT, &docs_hard_limit_); params.get(PARAM_HNSW_SPARSE_STREAMER_EF, &ef_); @@ -56,9 +62,7 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, params.get(PARAM_HNSW_SPARSE_STREAMER_MAX_SCAN_LIMIT, &max_scan_limit_); params.get(PARAM_HNSW_SPARSE_STREAMER_MIN_SCAN_LIMIT, &min_scan_limit_); params.get(PARAM_HNSW_SPARSE_STREAMER_CHECK_CRC_ENABLE, &check_crc_enabled_); - ratio = HnswSparseEntity::kDefaultNeighborPruneRatio; - params.get(PARAM_HNSW_SPARSE_STREAMER_NEIGHBOR_PRUNE_RATIO, &ratio); - size_t prune_cnt = ratio * neighbor_cnt_; + params.get(PARAM_HNSW_SPARSE_STREAMER_CHUNK_SIZE, &chunk_size_); params.get(PARAM_HNSW_SPARSE_STREAMER_FILTER_SAME_KEY, &filter_same_key_); params.get(PARAM_HNSW_SPARSE_STREAMER_GET_VECTOR_ENABLE, @@ -88,29 +92,29 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, if (ef_construction_ == 0U) { ef_construction_ = HnswSparseEntity::kDefaultEfConstruction; } - if (neighbor_cnt_ == 0U) { - neighbor_cnt_ = HnswSparseEntity::kDefaultNeighborCnt; + if (upper_max_neighbor_cnt_ == 0U) { + upper_max_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperMaxNeighborCnt; } - if (neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { + if (upper_max_neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { LOG_ERROR("[%s] must be in range (0,%d)", PARAM_HNSW_SPARSE_STREAMER_MAX_NEIGHBOR_COUNT.c_str(), HnswSparseEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (upper_neighbor_cnt_ == 0U) { - upper_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperNeighborCnt; + if (l0_max_neighbor_cnt_ == 0U) { + l0_max_neighbor_cnt_ = HnswSparseEntity::kDefaultL0MaxNeighborCnt; } - if (upper_neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { + if (l0_max_neighbor_cnt_ > HnswSparseEntity::kMaxNeighborCnt) { LOG_ERROR("UpperNeighborCnt must be in range (0,%d)", HnswSparseEntity::kMaxNeighborCnt); return IndexError_InvalidArgument; } - if (min_neighbor_cnt_ > neighbor_cnt_) { + if (min_neighbor_cnt_ > upper_max_neighbor_cnt_) { LOG_ERROR("[%s]-[%u] must be <= [%s]-[%u]", PARAM_HNSW_SPARSE_STREAMER_MIN_NEIGHBOR_COUNT.c_str(), min_neighbor_cnt_, PARAM_HNSW_SPARSE_STREAMER_MAX_NEIGHBOR_COUNT.c_str(), - neighbor_cnt_); + upper_max_neighbor_cnt_); return IndexError_InvalidArgument; } @@ -144,7 +148,7 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, } if (prune_cnt == 0UL) { - prune_cnt = upper_neighbor_cnt_; + prune_cnt = upper_max_neighbor_cnt_; } if (chunk_size_ == 0UL) { chunk_size_ = HnswSparseEntity::kDefaultChunkSize; @@ -164,8 +168,8 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, } entity_.set_ef_construction(ef_construction_); - entity_.set_neighbor_cnt(neighbor_cnt_); - entity_.set_upper_neighbor_cnt(upper_neighbor_cnt_); + entity_.set_l0_neighbor_cnt(l0_max_neighbor_cnt_); + entity_.set_upper_neighbor_cnt(upper_max_neighbor_cnt_); entity_.set_scaling_factor(scaling_factor_); entity_.set_prune_cnt(prune_cnt); @@ -184,17 +188,17 @@ int HnswSparseStreamer::init(const IndexMeta &imeta, } LOG_DEBUG( "Init params: maxIndexSize=%zu docsHardLimit=%zu docsSoftLimit=%zu " - "efConstruction=%u ef=%u neighborCnt=%u upperNeighborCnt=%u " + "efConstruction=%u ef=%u l0NeighborCnt=%u upperNeighborCnt=%u " "scalingFactor=%u maxScanRatio=%.3f minScanLimit=%zu maxScanLimit=%zu " "bfEnabled=%d bruteFoceThreshold=%zu bfNegativeProbility=%.5f " "checkCrcEnabled=%d pruneSize=%zu chunkSize=%zu " "filterSameKey=%u getVectorEnabled=%u " "minNeighborCount=%u forcePadding=%u filteringRatio=%f", max_index_size_, docs_hard_limit_, docs_soft_limit_, ef_construction_, - ef_, neighbor_cnt_, upper_neighbor_cnt_, scaling_factor_, max_scan_ratio_, - min_scan_limit_, max_scan_limit_, bf_enabled_, bruteforce_threshold_, - bf_negative_prob_, check_crc_enabled_, prune_cnt, chunk_size_, - filter_same_key_, get_vector_enabled_, min_neighbor_cnt_, + ef_, l0_max_neighbor_cnt_, upper_max_neighbor_cnt_, scaling_factor_, + max_scan_ratio_, min_scan_limit_, max_scan_limit_, bf_enabled_, + bruteforce_threshold_, bf_negative_prob_, check_crc_enabled_, prune_cnt, + chunk_size_, filter_same_key_, get_vector_enabled_, min_neighbor_cnt_, force_padding_topk_enabled_, query_filtering_ratio_); alg_ = HnswSparseAlgorithm::UPointer(new HnswSparseAlgorithm(entity_)); @@ -228,8 +232,7 @@ int HnswSparseStreamer::cleanup(void) { max_index_size_ = 0UL; docs_hard_limit_ = HnswSparseEntity::kDefaultDocsHardLimit; docs_soft_limit_ = 0UL; - neighbor_cnt_ = HnswSparseEntity::kDefaultNeighborCnt; - upper_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperNeighborCnt; + upper_max_neighbor_cnt_ = HnswSparseEntity::kDefaultUpperMaxNeighborCnt; ef_ = HnswSparseEntity::kDefaultEf; ef_construction_ = HnswSparseEntity::kDefaultEfConstruction; bf_enabled_ = false; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.h index 20c2788..21c6be6 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer.h @@ -190,9 +190,10 @@ class HnswSparseStreamer : public IndexStreamer { size_t chunk_size_{HnswSparseEntity::kDefaultChunkSize}; size_t docs_hard_limit_{HnswSparseEntity::kDefaultDocsHardLimit}; size_t docs_soft_limit_{0UL}; - uint32_t neighbor_cnt_{HnswSparseEntity::kDefaultNeighborCnt}; uint32_t min_neighbor_cnt_{0u}; - uint32_t upper_neighbor_cnt_{HnswSparseEntity::kDefaultUpperNeighborCnt}; + uint32_t upper_max_neighbor_cnt_{ + HnswSparseEntity::kDefaultUpperMaxNeighborCnt}; + uint32_t l0_max_neighbor_cnt_{HnswSparseEntity::kDefaultL0MaxNeighborCnt}; uint32_t ef_{HnswSparseEntity::kDefaultEf}; uint32_t ef_construction_{HnswSparseEntity::kDefaultEfConstruction}; uint32_t scaling_factor_{HnswSparseEntity::kDefaultScalingFactor}; diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.cc b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.cc index 8299388..b60fe4e 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.cc +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.cc @@ -472,9 +472,9 @@ int HnswSparseStreamerEntity::open(IndexStorage::Pointer stg, bool check_crc) { } LOG_INFO( - "Open index, neighborCnt=%zu upperneighborCnt=%zu " + "Open index, l0NeighborCnt=%zu upperneighborCnt=%zu " "efConstruction=%zu curDocCnt=%u totalVecs=%u maxLevel=%u", - neighbor_cnt(), upper_neighbor_cnt(), ef_construction(), doc_cnt(), + l0_neighbor_cnt(), upper_neighbor_cnt(), ef_construction(), doc_cnt(), total_vecs, cur_max_level()); //! try to correct the docCnt if index not fully flushed if (doc_cnt() != total_vecs) { @@ -551,10 +551,10 @@ int HnswSparseStreamerEntity::dump(const IndexDumper::Pointer &dumper) { int HnswSparseStreamerEntity::check_hnsw_index( const HNSWSparseHeader *hd) const { - if (neighbor_cnt() != hd->neighbor_cnt() || + if (l0_neighbor_cnt() != hd->neighbor_cnt() || upper_neighbor_cnt() != hd->upper_neighbor_cnt()) { LOG_ERROR("Param neighbors:%zu:%zu mismatch index previous %zu:%zu", - neighbor_cnt(), upper_neighbor_cnt(), hd->neighbor_cnt(), + l0_neighbor_cnt(), upper_neighbor_cnt(), hd->neighbor_cnt(), hd->upper_neighbor_cnt()); return IndexError_Mismatch; } diff --git a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.h b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.h index 8351d08..97d321d 100644 --- a/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.h +++ b/src/core/algorithm/hnsw_sparse/hnsw_sparse_streamer_entity.h @@ -195,7 +195,7 @@ class HnswSparseStreamerEntity : public HnswSparseEntity { //! Get neighbors size inline size_t neighbors_size() const { - return sizeof(NeighborsHeader) + neighbor_cnt() * sizeof(node_id_t); + return sizeof(NeighborsHeader) + l0_neighbor_cnt() * sizeof(node_id_t); } //! Get upper neighbors size