vectordb-bench 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vectordb_bench/__init__.py +14 -3
- vectordb_bench/backend/cases.py +34 -13
- vectordb_bench/backend/clients/__init__.py +6 -1
- vectordb_bench/backend/clients/api.py +12 -8
- vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py +4 -2
- vectordb_bench/backend/clients/milvus/milvus.py +17 -10
- vectordb_bench/backend/clients/pgvector/config.py +49 -0
- vectordb_bench/backend/clients/pgvector/pgvector.py +171 -0
- vectordb_bench/backend/clients/pinecone/pinecone.py +4 -3
- vectordb_bench/backend/clients/qdrant_cloud/config.py +20 -2
- vectordb_bench/backend/clients/qdrant_cloud/qdrant_cloud.py +11 -11
- vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py +5 -5
- vectordb_bench/backend/clients/zilliz_cloud/zilliz_cloud.py +3 -1
- vectordb_bench/backend/dataset.py +99 -149
- vectordb_bench/backend/result_collector.py +2 -2
- vectordb_bench/backend/runner/mp_runner.py +29 -13
- vectordb_bench/backend/runner/serial_runner.py +69 -51
- vectordb_bench/backend/task_runner.py +43 -48
- vectordb_bench/frontend/components/get_results/saveAsImage.py +4 -2
- vectordb_bench/frontend/const/dbCaseConfigs.py +35 -4
- vectordb_bench/frontend/const/dbPrices.py +5 -33
- vectordb_bench/frontend/const/styles.py +9 -3
- vectordb_bench/metric.py +0 -1
- vectordb_bench/models.py +12 -8
- vectordb_bench/results/dbPrices.json +32 -0
- vectordb_bench/results/getLeaderboardData.py +52 -0
- vectordb_bench/results/leaderboard.json +1 -0
- vectordb_bench/results/{result_20230609_standard.json → result_20230705_standard.json} +670 -214
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/METADATA +98 -13
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/RECORD +34 -29
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/LICENSE +0 -0
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/WHEEL +0 -0
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/entry_points.txt +0 -0
- {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/top_level.txt +0 -0
@@ -4,94 +4,96 @@ import traceback
|
|
4
4
|
import concurrent
|
5
5
|
import multiprocessing as mp
|
6
6
|
import math
|
7
|
+
import psutil
|
8
|
+
|
7
9
|
import numpy as np
|
8
10
|
import pandas as pd
|
9
11
|
|
10
12
|
from ..clients import api
|
11
13
|
from ...metric import calc_recall
|
12
|
-
from ...models import LoadTimeoutError
|
14
|
+
from ...models import LoadTimeoutError, PerformanceTimeoutError
|
13
15
|
from .. import utils
|
14
16
|
from ... import config
|
17
|
+
from vectordb_bench.backend.dataset import DatasetManager
|
15
18
|
|
16
19
|
NUM_PER_BATCH = config.NUM_PER_BATCH
|
17
|
-
LOAD_TIMEOUT = 24 * 60 * 60
|
18
20
|
LOAD_MAX_TRY_COUNT = 10
|
19
21
|
WAITTING_TIME = 60
|
20
22
|
|
21
23
|
log = logging.getLogger(__name__)
|
22
24
|
|
23
|
-
|
24
25
|
class SerialInsertRunner:
|
25
|
-
def __init__(self, db: api.VectorDB,
|
26
|
-
|
26
|
+
def __init__(self, db: api.VectorDB, dataset: DatasetManager, normalize: bool, timeout: float | None = None):
|
27
|
+
self.timeout = timeout if isinstance(timeout, (int, float)) else None
|
28
|
+
self.dataset = dataset
|
27
29
|
self.db = db
|
28
|
-
self.
|
29
|
-
self.train_id = train_id
|
30
|
-
|
31
|
-
self.seq_batches = math.ceil(len(train_emb)/NUM_PER_BATCH)
|
30
|
+
self.normalize = normalize
|
32
31
|
|
33
|
-
def
|
32
|
+
def task(self) -> int:
|
33
|
+
count = 0
|
34
34
|
with self.db.init():
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
log.debug(f"
|
35
|
+
log.info(f"({mp.current_process().name:16}) Start inserting embeddings in batch {config.NUM_PER_BATCH}")
|
36
|
+
start = time.perf_counter()
|
37
|
+
for data_df in self.dataset:
|
38
|
+
all_metadata = data_df['id'].tolist()
|
39
|
+
|
40
|
+
emb_np = np.stack(data_df['emb'])
|
41
|
+
if self.normalize:
|
42
|
+
log.debug("normalize the 100k train data")
|
43
|
+
all_embeddings = emb_np / np.linalg.norm(emb_np, axis=1)[:, np.newaxis].tolist()
|
44
|
+
else:
|
45
|
+
all_embeddings = emb_np.tolist()
|
46
|
+
del(emb_np)
|
47
|
+
log.debug(f"batch dataset size: {len(all_embeddings)}, {len(all_metadata)}")
|
48
|
+
|
49
|
+
last_batch = self.dataset.data.size - count == len(all_metadata)
|
48
50
|
insert_count, error = self.db.insert_embeddings(
|
49
|
-
embeddings=
|
50
|
-
metadata=
|
51
|
+
embeddings=all_embeddings,
|
52
|
+
metadata=all_metadata,
|
53
|
+
last_batch=last_batch,
|
51
54
|
)
|
52
|
-
if error
|
55
|
+
if error is not None:
|
53
56
|
raise error
|
54
|
-
log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{num_conc_batches}], Finish inserting {len(metadata)} embeddings")
|
55
57
|
|
56
|
-
assert insert_count == len(
|
58
|
+
assert insert_count == len(all_metadata)
|
57
59
|
count += insert_count
|
58
|
-
|
59
|
-
|
60
|
+
if count % 100_000 == 0:
|
61
|
+
log.info(f"({mp.current_process().name:16}) Loaded {count} embeddings into VectorDB")
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
all_embeddings = self.shared_emb
|
63
|
+
log.info(f"({mp.current_process().name:16}) Finish loading all dataset into VectorDB, dur={time.perf_counter()-start}")
|
64
|
+
return count
|
64
65
|
|
66
|
+
def endless_insert_data(self, all_embeddings, all_metadata, left_id: int = 0) -> int:
|
67
|
+
with self.db.init():
|
65
68
|
# unique id for endlessness insertion
|
66
|
-
all_metadata = [i+left_id for i in
|
69
|
+
all_metadata = [i+left_id for i in all_metadata]
|
67
70
|
|
68
|
-
|
71
|
+
NUM_BATCHES = math.ceil(len(all_embeddings)/NUM_PER_BATCH)
|
69
72
|
log.info(f"({mp.current_process().name:16}) Start inserting {len(all_embeddings)} embeddings in batch {NUM_PER_BATCH}")
|
70
73
|
count = 0
|
71
|
-
for batch_id in range(
|
74
|
+
for batch_id in range(NUM_BATCHES):
|
72
75
|
retry_count = 0
|
73
76
|
already_insert_count = 0
|
74
77
|
metadata = all_metadata[batch_id*NUM_PER_BATCH : (batch_id+1)*NUM_PER_BATCH]
|
75
78
|
embeddings = all_embeddings[batch_id*NUM_PER_BATCH : (batch_id+1)*NUM_PER_BATCH]
|
76
79
|
|
77
|
-
log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{
|
80
|
+
log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{NUM_BATCHES}], Start inserting {len(metadata)} embeddings")
|
78
81
|
while retry_count < LOAD_MAX_TRY_COUNT:
|
79
|
-
previous_beg, current_beg = 0, 0
|
80
82
|
insert_count, error = self.db.insert_embeddings(
|
81
83
|
embeddings=embeddings[already_insert_count :],
|
82
84
|
metadata=metadata[already_insert_count :],
|
83
85
|
)
|
84
86
|
already_insert_count += insert_count
|
85
|
-
if error
|
87
|
+
if error is not None:
|
86
88
|
retry_count += 1
|
87
89
|
time.sleep(WAITTING_TIME)
|
88
|
-
|
90
|
+
|
89
91
|
log.info(f"Failed to insert data, try {retry_count} time")
|
90
92
|
if retry_count >= LOAD_MAX_TRY_COUNT:
|
91
93
|
raise error
|
92
94
|
else:
|
93
95
|
break
|
94
|
-
log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{
|
96
|
+
log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{NUM_BATCHES}], Finish inserting {len(metadata)} embeddings")
|
95
97
|
|
96
98
|
assert already_insert_count == len(metadata)
|
97
99
|
count += already_insert_count
|
@@ -102,30 +104,46 @@ class SerialInsertRunner:
|
|
102
104
|
def _insert_all_batches(self) -> int:
|
103
105
|
"""Performance case only"""
|
104
106
|
with concurrent.futures.ProcessPoolExecutor(mp_context=mp.get_context('spawn'), max_workers=1) as executor:
|
105
|
-
future = executor.submit(self.
|
106
|
-
|
107
|
-
|
107
|
+
future = executor.submit(self.task)
|
108
|
+
try:
|
109
|
+
count = future.result(timeout=self.timeout)
|
110
|
+
except TimeoutError as e:
|
111
|
+
msg = f"VectorDB load dataset timeout in {self.timeout}"
|
112
|
+
log.warning(msg)
|
113
|
+
for pid, _ in executor._processes.items():
|
114
|
+
psutil.Process(pid).kill()
|
115
|
+
raise PerformanceTimeoutError(msg) from e
|
116
|
+
except Exception as e:
|
117
|
+
log.warning(f"VectorDB load dataset error: {e}")
|
118
|
+
raise e from e
|
119
|
+
else:
|
120
|
+
return count
|
108
121
|
|
109
122
|
def run_endlessness(self) -> int:
|
110
123
|
"""run forever util DB raises exception or crash"""
|
124
|
+
# datasets for load tests are quite small, can fit into memory
|
125
|
+
# only 1 file
|
126
|
+
data_df = [data_df for data_df in self.dataset][0]
|
127
|
+
all_embeddings, all_metadata = np.stack(data_df["emb"]).tolist(), data_df['id'].tolist()
|
128
|
+
|
111
129
|
start_time = time.perf_counter()
|
112
130
|
max_load_count, times = 0, 0
|
113
131
|
try:
|
114
132
|
with self.db.init():
|
115
133
|
self.db.ready_to_load()
|
116
|
-
while time.perf_counter() - start_time <
|
117
|
-
count = self.endless_insert_data(left_id=max_load_count)
|
134
|
+
while time.perf_counter() - start_time < self.timeout:
|
135
|
+
count = self.endless_insert_data(all_embeddings, all_metadata, left_id=max_load_count)
|
118
136
|
max_load_count += count
|
119
137
|
times += 1
|
120
138
|
log.info(f"Loaded {times} entire dataset, current max load counts={utils.numerize(max_load_count)}, {max_load_count}")
|
121
|
-
raise LoadTimeoutError("capacity case load timeout and stop")
|
122
|
-
except LoadTimeoutError as e:
|
123
|
-
log.info("load timetout, stop the load case")
|
124
|
-
raise e from None
|
125
139
|
except Exception as e:
|
126
140
|
log.info(f"Capacity case load reach limit, insertion counts={utils.numerize(max_load_count)}, {max_load_count}, err={e}")
|
127
141
|
traceback.print_exc()
|
128
142
|
return max_load_count
|
143
|
+
else:
|
144
|
+
msg = f"capacity case load timeout in {self.timeout}s"
|
145
|
+
log.info(msg)
|
146
|
+
raise LoadTimeoutError(msg)
|
129
147
|
|
130
148
|
def run(self) -> int:
|
131
149
|
count, dur = self._insert_all_batches()
|
@@ -157,7 +175,7 @@ class SerialSearchRunner:
|
|
157
175
|
test_data, ground_truth = args
|
158
176
|
|
159
177
|
log.debug(f"test dataset size: {len(test_data)}")
|
160
|
-
log.
|
178
|
+
log.debug(f"ground truth size: {ground_truth.columns}, shape: {ground_truth.shape}")
|
161
179
|
|
162
180
|
latencies, recalls = [], []
|
163
181
|
for idx, emb in enumerate(test_data):
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
import psutil
|
2
3
|
import traceback
|
3
4
|
import concurrent
|
4
5
|
import numpy as np
|
@@ -7,7 +8,7 @@ from enum import Enum, auto
|
|
7
8
|
from . import utils
|
8
9
|
from .cases import Case, CaseLabel
|
9
10
|
from ..base import BaseModel
|
10
|
-
from ..models import TaskConfig
|
11
|
+
from ..models import TaskConfig, PerformanceTimeoutError
|
11
12
|
|
12
13
|
from .clients import (
|
13
14
|
api,
|
@@ -92,80 +93,70 @@ class CaseRunner(BaseModel):
|
|
92
93
|
self._pre_run(drop_old)
|
93
94
|
|
94
95
|
if self.ca.label == CaseLabel.Load:
|
95
|
-
return self.
|
96
|
+
return self._run_capacity_case()
|
96
97
|
elif self.ca.label == CaseLabel.Performance:
|
97
98
|
return self._run_perf_case(drop_old)
|
98
99
|
else:
|
99
|
-
|
100
|
-
|
100
|
+
msg = f"unknown case type: {self.ca.label}"
|
101
|
+
log.warning(msg)
|
102
|
+
raise ValueError(msg)
|
101
103
|
|
102
|
-
|
103
|
-
|
104
|
-
""" run load cases
|
104
|
+
def _run_capacity_case(self) -> Metric:
|
105
|
+
""" run capacity cases
|
105
106
|
|
106
107
|
Returns:
|
107
108
|
Metric: the max load count
|
108
109
|
"""
|
109
110
|
log.info("Start capacity case")
|
110
|
-
# datasets for load tests are quite small, can fit into memory
|
111
|
-
# only 1 file
|
112
|
-
data_df = [data_df for data_df in self.ca.dataset][0]
|
113
|
-
|
114
|
-
all_embeddings, all_metadata = np.stack(data_df["emb"]).tolist(), data_df['id'].tolist()
|
115
|
-
runner = SerialInsertRunner(self.db, all_embeddings, all_metadata)
|
116
111
|
try:
|
112
|
+
runner = SerialInsertRunner(self.db, self.ca.dataset, self.normalize, self.ca.load_timeout)
|
117
113
|
count = runner.run_endlessness()
|
118
|
-
log.info(f"load reach limit: insertion counts={count}")
|
119
|
-
return Metric(max_load_count=count)
|
120
114
|
except Exception as e:
|
121
|
-
log.warning(f"run capacity case
|
115
|
+
log.warning(f"Failed to run capacity case, reason = {e}")
|
122
116
|
raise e from None
|
123
|
-
|
124
|
-
|
117
|
+
else:
|
118
|
+
log.info(f"Capacity case loading dataset reaches VectorDB's limit: max capacity = {count}")
|
119
|
+
return Metric(max_load_count=count)
|
125
120
|
|
126
121
|
def _run_perf_case(self, drop_old: bool = True) -> Metric:
|
122
|
+
""" run performance cases
|
123
|
+
|
124
|
+
Returns:
|
125
|
+
Metric: load_duration, recall, serial_latency_p99, and, qps
|
126
|
+
"""
|
127
127
|
try:
|
128
128
|
m = Metric()
|
129
129
|
if drop_old:
|
130
130
|
_, load_dur = self._load_train_data()
|
131
131
|
build_dur = self._optimize()
|
132
132
|
m.load_duration = round(load_dur+build_dur, 4)
|
133
|
+
log.info(
|
134
|
+
f"Finish loading the entire dataset into VectorDB,"
|
135
|
+
f" insert_duration={load_dur}, optimize_duration={build_dur}"
|
136
|
+
f" load_duration(insert + optimize) = {m.load_duration}"
|
137
|
+
)
|
133
138
|
|
134
139
|
self._init_search_runner()
|
135
140
|
m.recall, m.serial_latency_p99 = self._serial_search()
|
136
141
|
m.qps = self._conc_search()
|
137
|
-
|
138
|
-
log.info(f"got results: {m}")
|
139
|
-
return m
|
140
142
|
except Exception as e:
|
141
|
-
log.warning(f"performance case
|
143
|
+
log.warning(f"Failed to run performance case, reason = {e}")
|
142
144
|
traceback.print_exc()
|
143
|
-
raise e
|
145
|
+
raise e from None
|
146
|
+
else:
|
147
|
+
log.info(f"Performance case got result: {m}")
|
148
|
+
return m
|
144
149
|
|
145
150
|
@utils.time_it
|
146
151
|
def _load_train_data(self):
|
147
152
|
"""Insert train data and get the insert_duration"""
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
all_embeddings = emb_np / np.linalg.norm(emb_np, axis=1)[:, np.newaxis].tolist()
|
156
|
-
else:
|
157
|
-
all_embeddings = emb_np.tolist()
|
158
|
-
|
159
|
-
del(emb_np)
|
160
|
-
log.debug(f"normalized size: {len(all_embeddings)}, {len(all_metadata)}")
|
161
|
-
|
162
|
-
runner = SerialInsertRunner(self.db, all_embeddings, all_metadata)
|
163
|
-
runner.run()
|
164
|
-
except Exception as e:
|
165
|
-
raise e from None
|
166
|
-
finally:
|
167
|
-
runner = None
|
168
|
-
|
153
|
+
try:
|
154
|
+
runner = SerialInsertRunner(self.db, self.ca.dataset, self.normalize, self.ca.load_timeout)
|
155
|
+
runner.run()
|
156
|
+
except Exception as e:
|
157
|
+
raise e from None
|
158
|
+
finally:
|
159
|
+
runner = None
|
169
160
|
|
170
161
|
def _serial_search(self) -> tuple[float, float]:
|
171
162
|
"""Performance serial tests, search the entire test data once,
|
@@ -198,17 +189,21 @@ class CaseRunner(BaseModel):
|
|
198
189
|
|
199
190
|
@utils.time_it
|
200
191
|
def _task(self) -> None:
|
201
|
-
""""""
|
202
192
|
with self.db.init():
|
203
|
-
self.db.
|
193
|
+
self.db.optimize()
|
204
194
|
|
205
195
|
def _optimize(self) -> float:
|
206
196
|
with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
|
207
197
|
future = executor.submit(self._task)
|
208
198
|
try:
|
209
|
-
return future.result()[1]
|
199
|
+
return future.result(timeout=self.ca.optimize_timeout)[1]
|
200
|
+
except TimeoutError as e:
|
201
|
+
log.warning(f"VectorDB optimize timeout in {self.ca.optimize_timeout}")
|
202
|
+
for pid, _ in executor._processes.items():
|
203
|
+
psutil.Process(pid).kill()
|
204
|
+
raise PerformanceTimeoutError("Performance case optimize timeout") from e
|
210
205
|
except Exception as e:
|
211
|
-
log.warning(f"VectorDB
|
206
|
+
log.warning(f"VectorDB optimize error: {e}")
|
212
207
|
raise e from None
|
213
208
|
|
214
209
|
def _init_search_runner(self):
|
@@ -32,7 +32,8 @@ const stApp = streamlitDoc.querySelector('.main > .block-container');
|
|
32
32
|
const buttons = Array.from(streamlitDoc.querySelectorAll('.stButton > button'));
|
33
33
|
const imgButton = buttons.find(el => el.innerText === '{buttonText}');
|
34
34
|
|
35
|
-
imgButton
|
35
|
+
if (imgButton)
|
36
|
+
imgButton.innerText = 'Creating Image...';
|
36
37
|
|
37
38
|
html2canvas(stApp, {{ allowTaint: false, useCORS: true }}).then(function (canvas) {{
|
38
39
|
a = document.createElement('a');
|
@@ -40,7 +41,8 @@ html2canvas(stApp, {{ allowTaint: false, useCORS: true }}).then(function (canvas
|
|
40
41
|
a.download = '{pageName}.png';
|
41
42
|
a.click();
|
42
43
|
|
43
|
-
imgButton
|
44
|
+
if (imgButton)
|
45
|
+
imgButton.innerText = '{buttonText}';
|
44
46
|
}})
|
45
47
|
</script>""",
|
46
48
|
height=0,
|
@@ -29,6 +29,7 @@ CASE_LIST_WITH_DIVIDER = [
|
|
29
29
|
|
30
30
|
CASE_LIST = [item for item in CASE_LIST_WITH_DIVIDER if isinstance(item, CaseType)]
|
31
31
|
|
32
|
+
|
32
33
|
class InputType(IntEnum):
|
33
34
|
Text = 20001
|
34
35
|
Number = 20002
|
@@ -185,6 +186,26 @@ CaseConfigParamInput_Nprobe = CaseConfigInput(
|
|
185
186
|
== IndexType.IVFFlat.value,
|
186
187
|
)
|
187
188
|
|
189
|
+
CaseConfigParamInput_Lists = CaseConfigInput(
|
190
|
+
label=CaseConfigParamType.lists,
|
191
|
+
inputType=InputType.Number,
|
192
|
+
inputConfig={
|
193
|
+
"min": 1,
|
194
|
+
"max": 65536,
|
195
|
+
"value": 10,
|
196
|
+
},
|
197
|
+
)
|
198
|
+
|
199
|
+
CaseConfigParamInput_Probes = CaseConfigInput(
|
200
|
+
label=CaseConfigParamType.probes,
|
201
|
+
inputType=InputType.Number,
|
202
|
+
inputConfig={
|
203
|
+
"min": 1,
|
204
|
+
"max": 65536,
|
205
|
+
"value": 1,
|
206
|
+
},
|
207
|
+
)
|
208
|
+
|
188
209
|
|
189
210
|
MilvusLoadConfig = [
|
190
211
|
CaseConfigParamInput_IndexType,
|
@@ -192,8 +213,6 @@ MilvusLoadConfig = [
|
|
192
213
|
CaseConfigParamInput_EFConstruction_Milvus,
|
193
214
|
CaseConfigParamInput_Nlist,
|
194
215
|
]
|
195
|
-
|
196
|
-
|
197
216
|
MilvusPerformanceConfig = [
|
198
217
|
CaseConfigParamInput_IndexType,
|
199
218
|
CaseConfigParamInput_M,
|
@@ -208,7 +227,6 @@ WeaviateLoadConfig = [
|
|
208
227
|
CaseConfigParamInput_MaxConnections,
|
209
228
|
CaseConfigParamInput_EFConstruction_Weaviate,
|
210
229
|
]
|
211
|
-
|
212
230
|
WeaviatePerformanceConfig = [
|
213
231
|
CaseConfigParamInput_MaxConnections,
|
214
232
|
CaseConfigParamInput_EFConstruction_Weaviate,
|
@@ -216,13 +234,15 @@ WeaviatePerformanceConfig = [
|
|
216
234
|
]
|
217
235
|
|
218
236
|
ESLoadingConfig = [CaseConfigParamInput_EFConstruction_ES, CaseConfigParamInput_M_ES]
|
219
|
-
|
220
237
|
ESPerformanceConfig = [
|
221
238
|
CaseConfigParamInput_EFConstruction_ES,
|
222
239
|
CaseConfigParamInput_M_ES,
|
223
240
|
CaseConfigParamInput_NumCandidates_ES,
|
224
241
|
]
|
225
242
|
|
243
|
+
PgVectorLoadingConfig = [CaseConfigParamInput_Lists]
|
244
|
+
PgVectorPerformanceConfig = [CaseConfigParamInput_Lists, CaseConfigParamInput_Probes]
|
245
|
+
|
226
246
|
CASE_CONFIG_MAP = {
|
227
247
|
DB.Milvus: {
|
228
248
|
CaseType.CapacityDim960: MilvusLoadConfig,
|
@@ -257,4 +277,15 @@ CASE_CONFIG_MAP = {
|
|
257
277
|
CaseType.Performance10M99P: ESPerformanceConfig,
|
258
278
|
CaseType.Performance1M99P: ESPerformanceConfig,
|
259
279
|
},
|
280
|
+
DB.PgVector: {
|
281
|
+
CaseType.CapacityDim960: PgVectorLoadingConfig,
|
282
|
+
CaseType.CapacityDim128: PgVectorLoadingConfig,
|
283
|
+
CaseType.Performance100M: PgVectorPerformanceConfig,
|
284
|
+
CaseType.Performance10M: PgVectorPerformanceConfig,
|
285
|
+
CaseType.Performance1M: PgVectorPerformanceConfig,
|
286
|
+
CaseType.Performance10M1P: PgVectorPerformanceConfig,
|
287
|
+
CaseType.Performance1M1P: PgVectorPerformanceConfig,
|
288
|
+
CaseType.Performance10M99P: PgVectorPerformanceConfig,
|
289
|
+
CaseType.Performance1M99P: PgVectorPerformanceConfig,
|
290
|
+
},
|
260
291
|
}
|
@@ -1,34 +1,6 @@
|
|
1
|
-
from vectordb_bench
|
1
|
+
from vectordb_bench import config
|
2
|
+
import ujson
|
3
|
+
import pathlib
|
2
4
|
|
3
|
-
|
4
|
-
DB_DBLABEL_TO_PRICE =
|
5
|
-
DB.Milvus.value: {},
|
6
|
-
DB.ZillizCloud.value: {
|
7
|
-
"1cu-perf": 0.159,
|
8
|
-
"8cu-perf": 1.272,
|
9
|
-
"1cu-cap": 0.159,
|
10
|
-
"2cu-cap": 0.318,
|
11
|
-
},
|
12
|
-
DB.WeaviateCloud.value: {
|
13
|
-
# "sandox": 0,
|
14
|
-
"standard": 10.10,
|
15
|
-
"bus_crit": 32.60,
|
16
|
-
},
|
17
|
-
DB.ElasticCloud.value: {
|
18
|
-
"upTo2.5c8g": 0.4793,
|
19
|
-
},
|
20
|
-
DB.QdrantCloud.value: {
|
21
|
-
"0.5c4g-1node": 0.052,
|
22
|
-
"2c8g-1node": 0.166,
|
23
|
-
"4c16g-5node": 1.426,
|
24
|
-
},
|
25
|
-
DB.Pinecone.value: {
|
26
|
-
"s1.x1": 0.0973,
|
27
|
-
"s1.x2": 0.194,
|
28
|
-
"p1.x1": 0.0973,
|
29
|
-
"p2.x1": 0.146,
|
30
|
-
"p2.x1-8node": 1.168,
|
31
|
-
"p1.x1-8node": 0.779,
|
32
|
-
"s1.x1-2node": 0.195,
|
33
|
-
},
|
34
|
-
}
|
5
|
+
with open(pathlib.Path(config.RESULTS_LOCAL_DIR, "dbPrices.json")) as f:
|
6
|
+
DB_DBLABEL_TO_PRICE = ujson.load(f)
|
@@ -32,21 +32,27 @@ PAGE_TITLE = "VectorDB Benchmark"
|
|
32
32
|
FAVICON = "https://assets.zilliz.com/favicon_f7f922fe27.png"
|
33
33
|
HEADER_ICON = "https://assets.zilliz.com/vdb_benchmark_db790b5387.png"
|
34
34
|
|
35
|
+
# RedisCloud icon: https://assets.zilliz.com/Redis_Cloud_74b8bfef39.png
|
36
|
+
# Elasticsearch icon: https://assets.zilliz.com/elasticsearch_beffeadc29.png
|
37
|
+
# Chroma icon: https://assets.zilliz.com/chroma_ceb3f06ed7.png
|
35
38
|
DB_TO_ICON = {
|
36
39
|
DB.Milvus: "https://assets.zilliz.com/milvus_c30b0d1994.png",
|
37
40
|
DB.ZillizCloud: "https://assets.zilliz.com/zilliz_5f4cc9b050.png",
|
38
|
-
DB.ElasticCloud: "https://assets.zilliz.com/
|
41
|
+
DB.ElasticCloud: "https://assets.zilliz.com/Elatic_Cloud_dad8d6a3a3.png",
|
39
42
|
DB.Pinecone: "https://assets.zilliz.com/pinecone_94d8154979.png",
|
40
43
|
DB.QdrantCloud: "https://assets.zilliz.com/qdrant_b691674fcd.png",
|
41
44
|
DB.WeaviateCloud: "https://assets.zilliz.com/weaviate_4f6f171ebe.png",
|
45
|
+
DB.PgVector: "https://assets.zilliz.com/PG_Vector_d464f2ef5f.png",
|
42
46
|
}
|
43
47
|
|
44
|
-
|
48
|
+
# RedisCloud color: #0D6EFD
|
49
|
+
# Chroma color: #FFC107
|
45
50
|
COLOR_MAP = {
|
46
51
|
DB.Milvus.value: "#0DCAF0",
|
47
52
|
DB.ZillizCloud.value: "#0D6EFD",
|
48
|
-
DB.ElasticCloud.value: "#
|
53
|
+
DB.ElasticCloud.value: "#04D6C8",
|
49
54
|
DB.Pinecone.value: "#6610F2",
|
50
55
|
DB.QdrantCloud.value: "#D91AD9",
|
51
56
|
DB.WeaviateCloud.value: "#20C997",
|
57
|
+
DB.PgVector.value: "#4C779A",
|
52
58
|
}
|
vectordb_bench/metric.py
CHANGED
vectordb_bench/models.py
CHANGED
@@ -43,6 +43,8 @@ class CaseConfigParamType(Enum):
|
|
43
43
|
Nprobe = "nprobe"
|
44
44
|
MaxConnections = "maxConnections"
|
45
45
|
numCandidates = "num_candidates"
|
46
|
+
lists = "lists"
|
47
|
+
probes = "probes"
|
46
48
|
|
47
49
|
|
48
50
|
class CustomizedCase(BaseModel):
|
@@ -167,20 +169,21 @@ class TestResult(BaseModel):
|
|
167
169
|
max_qps = max(map(len, [str(f.metrics.qps) for f in filtered_results])) + 3
|
168
170
|
max_recall = max(map(len, [str(f.metrics.recall) for f in filtered_results])) + 3
|
169
171
|
|
170
|
-
max_db_labels = 8 if max_db_labels
|
171
|
-
max_load_dur = 11 if max_load_dur
|
172
|
-
max_qps = 10 if max_qps
|
173
|
-
max_recall = 13 if max_recall
|
172
|
+
max_db_labels = 8 if max_db_labels < 8 else max_db_labels
|
173
|
+
max_load_dur = 11 if max_load_dur < 11 else max_load_dur
|
174
|
+
max_qps = 10 if max_qps < 10 else max_qps
|
175
|
+
max_recall = 13 if max_recall < 13 else max_recall
|
174
176
|
|
175
|
-
LENGTH = (max_db, max_db_labels, max_case, len(self.task_label), max_load_dur, max_qps, 15, max_recall, 14)
|
177
|
+
LENGTH = (max_db, max_db_labels, max_case, len(self.task_label), max_load_dur, max_qps, 15, max_recall, 14, 5)
|
176
178
|
|
177
179
|
DATA_FORMAT = (
|
178
|
-
f"%-{max_db}s | %-{max_db_labels}s %-{max_case}s %-{len(self.task_label)}s
|
179
|
-
f"| %-{max_load_dur}s %-{max_qps}s %-15s %-{max_recall}s %-14s"
|
180
|
+
f"%-{max_db}s | %-{max_db_labels}s %-{max_case}s %-{len(self.task_label)}s"
|
181
|
+
f" | %-{max_load_dur}s %-{max_qps}s %-15s %-{max_recall}s %-14s"
|
182
|
+
f" | %-5s"
|
180
183
|
)
|
181
184
|
|
182
185
|
TITLE = DATA_FORMAT % (
|
183
|
-
"DB", "db_label", "case", "label", "load_dur", "qps", "latency(p99)", "recall", "max_load_count")
|
186
|
+
"DB", "db_label", "case", "label", "load_dur", "qps", "latency(p99)", "recall", "max_load_count", "label")
|
184
187
|
SPLIT = DATA_FORMAT%tuple(map(lambda x:"-"*x, LENGTH))
|
185
188
|
SUMMERY_FORMAT = ("Task summery: run_id=%s, task_label=%s") % (self.run_id[:5], self.task_label)
|
186
189
|
fmt = [SUMMERY_FORMAT, TITLE, SPLIT]
|
@@ -197,6 +200,7 @@ class TestResult(BaseModel):
|
|
197
200
|
f.metrics.serial_latency_p99,
|
198
201
|
f.metrics.recall,
|
199
202
|
f.metrics.max_load_count,
|
203
|
+
f.label.value,
|
200
204
|
))
|
201
205
|
|
202
206
|
tmp_logger = logging.getLogger("no_color")
|
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"Milvus": {},
|
3
|
+
"ZillizCloud": {
|
4
|
+
"1cu-perf": 0.159,
|
5
|
+
"8cu-perf": 1.272,
|
6
|
+
"1cu-cap": 0.159,
|
7
|
+
"2cu-cap": 0.318
|
8
|
+
},
|
9
|
+
"WeaviateCloud": {
|
10
|
+
"standard": 10.1,
|
11
|
+
"bus_crit": 32.6
|
12
|
+
},
|
13
|
+
"ElasticCloud": {
|
14
|
+
"upTo2.5c8g": 0.4793
|
15
|
+
},
|
16
|
+
"QdrantCloud": {
|
17
|
+
"0.5c4g-1node": 0.052,
|
18
|
+
"2c8g-1node": 0.166,
|
19
|
+
"4c16g-1node": 0.2852,
|
20
|
+
"4c16g-5node": 1.426
|
21
|
+
},
|
22
|
+
"Pinecone": {
|
23
|
+
"s1.x1": 0.0973,
|
24
|
+
"s1.x2": 0.194,
|
25
|
+
"p1.x1": 0.0973,
|
26
|
+
"p2.x1": 0.146,
|
27
|
+
"p2.x1-8node": 1.168,
|
28
|
+
"p1.x1-8node": 0.779,
|
29
|
+
"s1.x1-2node": 0.195
|
30
|
+
},
|
31
|
+
"PgVector": {}
|
32
|
+
}
|