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
This commit is contained in:
parent
9c106775df
commit
84b5f03f5e
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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_{};
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<SegmentPointer> &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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<SegmentPointer> &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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue