Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sq for hnsw #311

Merged
merged 1 commit into from
Mar 8, 2024
Merged

Add sq for hnsw #311

merged 1 commit into from
Mar 8, 2024

Conversation

hhy3
Copy link
Collaborator

@hhy3 hhy3 commented Dec 19, 2023

issue: #312

Copy link

mergify bot commented Dec 19, 2023

@hhy3 🔍 Important: PR Classification Needed!

For efficient project management and a seamless review process, it's essential to classify your PR correctly. Here's how:

  1. If you're fixing a bug, label it as kind/bug.
  2. For small tweaks (less than 20 lines without altering any functionality), please use kind/improvement.
  3. Significant changes that don't modify existing functionalities should be tagged as kind/enhancement.
  4. Adjusting APIs or changing functionality? Go with kind/feature.

For any PR outside the kind/improvement category, ensure you link to the associated issue using the format: “issue: #”.

Thanks for your efforts and contribution to the community!.

@hhy3
Copy link
Collaborator Author

hhy3 commented Dec 19, 2023

/kind feature

@@ -43,6 +43,7 @@ constexpr const char* INDEX_RAFT_IVFPQ = "GPU_RAFT_IVF_PQ";
constexpr const char* INDEX_RAFT_CAGRA = "GPU_RAFT_CAGRA";

constexpr const char* INDEX_HNSW = "HNSW";
constexpr const char* INDEX_HNSW_SQ = "HNSW_SQ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not HNSW_SQ8?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be HNSW_SQ8

hash_u8_vec(const uint8_t* x, size_t d) {
uint64_t h = seed;
for (size_t i = 0; i < d; ++i) {
h = h * 13331 + *(uint8_t*)(x + i);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

h = h * 13331 + x[i];

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@@ -215,5 +215,28 @@ fvec_L2sqr_batch_4_avx(const float* x, const float* y0, const float* y1, const f
}
FAISS_PRAGMA_IMPRECISE_FUNCTION_END

// trust the compiler to unroll this properly
int32_t
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please confirm that it is int32_t, not uint32_t

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems no big difference between int32_t and uint32_t

data_norm = knowhere::CopyAndNormalizeVecs(from, 1, dim);
from = data_norm.get();
}
for (size_t i = 0; i < dim; ++i) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please confirm that is it really int8_t, not uint8_t.
Classical SQ implementation uses uint8_t and maps into [0, 255] range, not [-128, 127].

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQ used there is symmetric version which maps to [-127, 127], which unify ip and l2 metric and simplify implementation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be good to add some rough comments about SQ types here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@@ -85,6 +85,14 @@ class HnswConfig : public BaseConfig {
}
};

class HnswSQConfig : public HnswConfig {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use the same user api with scann, by using "with_raw_data" option? Also, do we need another refine param like "reorder_k" used in scann

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

mutable knowhere::lru_cache<uint64_t, tableint> lru_cache;

void
trainSQuant(const float* train_data, size_t ntrain) {
size_t dim = *(size_t*)dist_func_param_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add alpha_ = 0; here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

}
for (size_t i = 0; i < dim; ++i) {
float x = from[i] / alpha_;
if (x > 1.0f) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this implementation assumes that the data distribution is symmetric around 0, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's the most common scenario

_mm_prefetch(getSQDataByInternalId(id), _MM_HINT_T0);
} else {
_mm_prefetch(getDataByInternalId(id), _MM_HINT_T0);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just mentioning that this is x86-only code, unless USE_PREFETCH is enabled for x86 only as well

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to centralized these platform related code just like what we do for distance hook. Created an issue to track #416.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the hook mechanism for prefetching is a horrible idea

class CosineSpace : public SpaceInterface<float> {
DISTFUNC<float> fstdistfunc_;
DISTFUNC<float> fstdistfunc_sq_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of an additional entity fstdistfunc_sq_ is needed, because SpaceInterface<> struct does not distinguish between underlying datatypes, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@@ -27,6 +27,7 @@
#include "knowhere/utils.h"

namespace knowhere {
template <bool USE_SQ>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specify SQ8 here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@@ -477,7 +489,7 @@ class HnswIndexNode : public IndexNode {

std::string
Type() const override {
return knowhere::IndexEnum::INDEX_HNSW;
return USE_SQ ? knowhere::IndexEnum::INDEX_HNSW_SQ8 : knowhere::IndexEnum::INDEX_HNSW;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use constexpr

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@@ -27,6 +27,7 @@
#include "knowhere/utils.h"

namespace knowhere {
template <bool USE_SQ>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a enum class type for this, in case we have SQ8, SQ4, PQ in the future

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@@ -67,11 +67,13 @@ class HierarchicalNSW : public AlgorithmInterface<dist_t> {
}

HierarchicalNSW(SpaceInterface<dist_t>* s, size_t max_elements, size_t M = 16, size_t ef_construction = 200,
size_t random_seed = 100)
bool use_sq = false, bool use_sq_refine = false, size_t random_seed = 100)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two new params are coupled. Let's make a new constructor for SQ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or why not make SQ a template the same as HNSWIndexNode

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

offsetData_ = size_links_level0_;
if (use_sq) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixed usage of use_sq and sq_enabled_

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll fix it

mutable knowhere::lru_cache<uint64_t, tableint> lru_cache;

void
trainSQuant(const float* train_data, size_t ntrain) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are adopting symmetrical global SQ. Plz make a note here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

data_norm = knowhere::CopyAndNormalizeVecs(from, 1, dim);
from = data_norm.get();
}
for (size_t i = 0; i < dim; ++i) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be good to add some rough comments about SQ types here.

_mm_prefetch(getSQDataByInternalId(id), _MM_HINT_T0);
} else {
_mm_prefetch(getDataByInternalId(id), _MM_HINT_T0);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to centralized these platform related code just like what we do for distance hook. Created an issue to track #416.

Signed-off-by: zh Wang <[email protected]>
@liliu-z
Copy link
Collaborator

liliu-z commented Mar 8, 2024

/lgtm

@sre-ci-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hhy3, liliu-z

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sre-ci-robot sre-ci-robot merged commit e44ba8d into zilliztech:main Mar 8, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants