Skip to content

Added PQ INT8 quantizer in Turbo - #554

Open
zzlin237 wants to merge 132 commits into
alibaba:mainfrom
zzlin237:refactor/turbo_pq
Open

Added PQ INT8 quantizer in Turbo#554
zzlin237 wants to merge 132 commits into
alibaba:mainfrom
zzlin237:refactor/turbo_pq

Conversation

@zzlin237

@zzlin237 zzlin237 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

The basic PQ has been implemented, but its QPS requires further optimization. The recall test results for the current implementation are as follows:

HNSW

Configuration:

  • M: 50
  • efConstruction: 500
  • efSearch: 400
  • topk: 50

L2 Metric

Datasets Quantizer Recall(%) Refiner Recall (%)
Sift kNone 100 -
Sift kPQ + int8 + 64 92.02 100
Sift kPQ + int8 + 32 78.44 99.98
Sift kPQ + int8 + 16 63.68 98.82
Gist kNone 98.576 -
Gist kPQ + int8 + 480 93.212 98.668
Gist kPQ + int8 + 240 76.676 98.688
Gist kPQ + int8 + 120 54.40 94.45

The results are generally consistent with previous tests. Under the int8 condition, using one subquantizer per 4 dimensions yields the best performance.

Cosine Metric

Option 1: Normalization + IP Metric

Option 2: Normalization + L2 Metric

Datasets Quantizer Recall(%) Refiner Recall (%)
Cohere kPQ + int8 + 192 + Option 2 86.03 99.73
Openai kPQ + int8 + 384 + Option 2 87.442 99.76

The performance of both options is nearly identical. Option 2 was ultimately adopted because the L2 metric also supports operations specific to L2, such as decentralization.

Inner Product Metric

Option 1: L2 for k-means + L2 encoding + IP for SDC calculation + IP for ADC calculation

Option 2: L2 for k-means + L2 encoding + L2 for SDC calculation + IP for ADC calculation |<---- faiss approach

Option 3: L2 for k-means + IP encoding + IP for SDC calculation + IP for ADC calculation

  • Failed approach: Due to mismatched encoding methods, recall dropped significantly.

Option 4: IP for k-means(sp) + IP encoding + IP for SDC calculation + IP for ADC calculation |<---- ivf porting approach

  • Failed approach: 1) Using IP in k-means leads to collapse, resulting in only one cluster containing points while all others remain empty; 2) Segmented clustering is unsuitable for spherical data. PQ performs segmentation before clustering. Even if the original vectors are normalized onto the unit sphere, their individual segments will no longer lie on the sphere.
Datasets Quantizer Recall(%) Refiner Recall (%)
Cohere kPQ + int8 + 192 + Option 1 79.904 99.658
Openai kPQ + int8 + 384 + Option 1 82.41 99.936
Cohere kPQ + int8 + 192 + Option 2 79.532 98.062
Openai kPQ + int8 + 384 + Option 2 82.266 99.65

Option 1 performs slightly better and is more semantically coherent: L2 is used for encoding, while IP is used for distance calculation.

Decentralization

Datasets Quantizer Recall(%) Refiner Recall (%)
Sift + L2 kPQ + int8 + 32 78.44 99.98
Sift + L2 kPQ + int8 + 32 + decentralization 78.56 99.96
Cohere + Cosine kPQ + int8 + 192 86.03 99.73
Cohere + Cosine kPQ + int8 + 192 + decentralization 86.088 99.738
Openai + Cosine kPQ + int8 + 384 87.54 99.78
Openai + Cosine kPQ + int8 + 384 + decentralization 87.68 99.78

Decentralization does not yield significant improvements, but support for it is retained nonetheless.

IVF

Configuration:

  • centroid_count: 128
  • nprobe: 16
  • topk: 50
  • ScaleFactor: 4

Option 1: Build codebooks using residuals, with each cluster assigned an independent codebook.

Option 2: Build codebooks using residuals, with all clusters sharing a single codebook. |<--- faiss approach

L2 Metric

Datasets Quantizer Recall(%) Refiner Recall (%)
Sift kNone 99.7 -
Sift IVFPQ + int8 + 32 + Option 1 86.18 99.54
Sift IVFPQ + int8 + 32 + Option 2 78.52 99.44
Gist kNone 96.412 -
Gist IVFPQ + int8 + 240 + Option 1 80.718 96.468
Gist IVFPQ + int8 + 240 + Option 2 77.49 96.244

Cosine Metric

Datasets Quantizer Recall(%) Refiner Recall (%)
Cohere kNone 99.576 -
Cohere IVFPQ + int8 + 192 + Option 1 85.174 94.37
Cohere IVFPQ + int8 + 192 + Option 2 83.916 94.09
Openai kNone 96.024 -
Openai IVFPQ + int8 + 384 + Option 1 87.218 96.196
Openai IVFPQ + int8 + 384 + Option 2 83.916 94.09

Inner Product Metric

Datasets Quantizer Recall(%) Refiner Recall (%)
Cohere kNone 87.632 -
Cohere IVFPQ + int8 + 192 + Option 1 81.43 95.696
Cohere IVFPQ + int8 + 192 + Option 2 79.74 95.77

The original IVFPQ+IP implementation uses MIPS, which results in lower recall.

Although Option 2 shows a slight decrease in Recall compared to Option 1, it significantly reduces training time and storage requirements while maintaining comparable Refiner Recall. Therefore, Option 2 was ultimately adopted.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 24, 2026 08:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@zzlin237
zzlin237 force-pushed the refactor/turbo_pq branch from 6a04997 to bedd5c3 Compare July 24, 2026 09:58
Copilot AI review requested due to automatic review settings July 24, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 27, 2026 06:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

Copilot AI review requested due to automatic review settings July 27, 2026 06:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

Copilot AI review requested due to automatic review settings July 28, 2026 08:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 28, 2026 10:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

//! via quantize_query(). Distance between a PQ code and a
//! query uses ADC (LUT look-up); distance between two PQ codes uses
//! SDC (centroid-to-centroid distance table).
class PqInt8Quantizer : public Quantizer {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PqInt8Quantizer 这个命名严谨吗?我理解只是每个子量化使用8bit code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

预计一共实现PqInt8Quantizer,PqInt4Quantizer和PqFastQuantizer三个量化器,对齐计划中的其他量化器取名:

  1. RecordInt8Quantizer,RecordInt4Quantizer

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PqInt4Quantizer和PqFastQuantizer是什么区别?

//! SDC (centroid-to-centroid distance table).
class PqInt8Quantizer : public Quantizer {
public:
PqInt8Quantizer() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. 为啥优先支持8bit?
  2. 除了支持8bit,后面还会支持其他数量的bit吗?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

  1. 当前的HNSW中最适用的就是8bit,比如faiss支持8bit和16bit,vsag支持4bit和8bit
  2. 后续会支持其他数量的bit,由于把4bit也一起改了会导致一次性更改的文件太多,不太好review,计划是分两次pr提交

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

好奇问问 为什么HNSW中最适用的就是8bit?

Comment thread src/turbo/distance/neon/pq_quantizer_int8/pq_distance.h
Copilot AI review requested due to automatic review settings July 30, 2026 03:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

# Conflicts:
#	src/include/zvec/core/interface/index_param.h
Copilot AI review requested due to automatic review settings July 30, 2026 06:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

return DistanceImpl{};
}

virtual DistanceImpl sym_distance(const void *query,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

in quantizer base class, calc_distance_dp_dp is used for calculating datapoint distance. is there any need to define a new interface for symmetric distance? or can define it within pq quantizer then be exposed as calc_distance_dp_dp?

const void *dist_table_v, size_t num_chunk,
float *out) {
constexpr size_t kNumCentroids = 256;
constexpr size_t kTablePerSub = kNumCentroids * kNumCentroids; // 65536

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

kTablePerSub,我们改成chunk吧这里,和外面统一

}

// ---------------------------------------------------------------------------
// Simple Lloyd's KMeans for one sub-quantizer (templated on data type T).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same.


void PqInt8Quantizer::quantize_data(const void *input, void *output) const {
uint8_t *code = reinterpret_cast<uint8_t *>(output);
const uint32_t elem_sz = element_size();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

elem_size

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants