Skip to content

Commit f830d8b

Browse files
BubbleCalwjones127
andauthored
perf!: store PQ codebook in global buffer instead of scheme metadata (#3829)
storing the PQ codebook in schema metadata is not efficient as it's in JSON format. this causes slow cold latency and high memory footprint when opening the index, so move it to global buffer, and store it as tensor protobuf format --------- Signed-off-by: BubbleCal <bubble-cal@outlook.com> Co-authored-by: Will Jones <willjones127@gmail.com>
1 parent 6f215d6 commit f830d8b

28 files changed

Lines changed: 873 additions & 580 deletions

File tree

.github/workflows/python.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,16 @@ jobs:
120120
- uses: ./.github/workflows/run_tests
121121
- name: Generate forward compatibility files
122122
run: python python/tests/forward_compat/datagen.py
123-
- name: Install old wheel
123+
- name: Run forward compatibility tests (pylance 0.16.0)
124124
run: |
125125
python -m venv venv
126126
source venv/bin/activate
127127
pip install pytest pylance==0.16.0
128-
- name: Run forward compatibility tests
128+
pytest python/tests/forward_compat --run-forward
129+
- name: Run forward compatibility tests (pylance 0.29.1.beta2)
129130
run: |
130131
source venv/bin/activate
132+
pip install pytest --pre --extra-index-url https://pypi.fury.io/lancedb/ pylance==0.29.1.beta2
131133
pytest python/tests/forward_compat --run-forward
132134
# Make sure wheels are not included in the Rust cache
133135
- name: Delete wheels

Cargo.lock

Lines changed: 17 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../rust-toolchain.toml

python/python/tests/forward_compat/datagen.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
from pathlib import Path
77

8+
import lance
89
import pyarrow as pa
10+
import pyarrow.compute as pc
911
from lance.file import LanceFileWriter
1012

1113

@@ -92,6 +94,32 @@ def write_large():
9294
writer.write_batch(build_large())
9395

9496

97+
def write_dataset_pq_buffer():
98+
# In https://github.com/lancedb/lance/pull/3829, we started storing the PQ
99+
# codebook in a global buffer instead of the schema metadata as JSON.
100+
101+
ndims = 32
102+
nvecs = 512
103+
104+
data = pa.table(
105+
{
106+
"id": pa.array(range(nvecs)),
107+
"vec": pa.FixedSizeListArray.from_arrays(
108+
pc.random(ndims * nvecs).cast(pa.float32()), ndims
109+
),
110+
}
111+
)
112+
113+
dataset = lance.write_dataset(data, get_path("pq_in_schema"))
114+
dataset.create_index(
115+
"vec",
116+
"IVF_PQ",
117+
num_partitions=1,
118+
num_sub_vectors=4,
119+
)
120+
121+
95122
if __name__ == "__main__":
96123
write_basic_types()
97124
write_large()
125+
write_dataset_pq_buffer()

python/python/tests/forward_compat/test_compat.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# SPDX-License-Identifier: Apache-2.0
22
# SPDX-FileCopyrightText: Copyright The Lance Authors
33

4+
import lance
5+
import pyarrow as pa
6+
import pyarrow.compute as pc
47
import pytest
58
from lance.file import LanceFileReader
9+
from packaging.version import Version
610

711
from .datagen import build_basic_types, build_large, get_path
812

@@ -18,3 +22,21 @@ def test_scans():
1822
expected_large = build_large()
1923
actual_large = LanceFileReader(str(get_path("large.lance"))).read_all().to_table()
2024
assert actual_large.equals(expected_large)
25+
26+
27+
@pytest.mark.forward
28+
@pytest.mark.skipif(
29+
Version(lance.__version__) < Version("0.29.1.beta2"), # at least 0.29.1-beta.2
30+
reason="Lance 0.29.1-beta.2 would ignore indices too new",
31+
)
32+
def test_pq_buffer():
33+
ds = lance.dataset(get_path("pq_in_schema"))
34+
# the index should be ignored, still able to query (brute force)
35+
q = pc.random(32).cast(pa.float32())
36+
ds.to_table(
37+
nearest={
38+
"q": q,
39+
"k": 4,
40+
"column": "vec",
41+
}
42+
)

python/rust-toolchain.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../rust-toolchain.toml

rust/lance-index/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl IndexType {
191191
| Self::IvfSq
192192
| Self::IvfPq
193193
| Self::IvfHnswSq
194-
| Self::IvfHnswPq => 0,
194+
| Self::IvfHnswPq => 1,
195195
}
196196
}
197197
}

rust/lance-index/src/vector/flat/index.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,8 @@ impl Quantization for FlatQuantizer {
208208
}))
209209
}
210210

211-
fn metadata(
212-
&self,
213-
_: Option<crate::vector::quantizer::QuantizationMetadata>,
214-
) -> Result<serde_json::Value> {
215-
let metadata = FlatMetadata { dim: self.dim };
216-
Ok(serde_json::to_value(metadata)?)
211+
fn metadata(&self, _: Option<crate::vector::quantizer::QuantizationMetadata>) -> FlatMetadata {
212+
FlatMetadata { dim: self.dim }
217213
}
218214

219215
fn metadata_key() -> &'static str {
@@ -290,12 +286,8 @@ impl Quantization for FlatBinQuantizer {
290286
}))
291287
}
292288

293-
fn metadata(
294-
&self,
295-
_: Option<crate::vector::quantizer::QuantizationMetadata>,
296-
) -> Result<serde_json::Value> {
297-
let metadata = FlatMetadata { dim: self.dim };
298-
Ok(serde_json::to_value(metadata)?)
289+
fn metadata(&self, _: Option<crate::vector::quantizer::QuantizationMetadata>) -> FlatMetadata {
290+
FlatMetadata { dim: self.dim }
299291
}
300292

301293
fn metadata_key() -> &'static str {

0 commit comments

Comments
 (0)