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.
Files changed (34) hide show
  1. vectordb_bench/__init__.py +14 -3
  2. vectordb_bench/backend/cases.py +34 -13
  3. vectordb_bench/backend/clients/__init__.py +6 -1
  4. vectordb_bench/backend/clients/api.py +12 -8
  5. vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py +4 -2
  6. vectordb_bench/backend/clients/milvus/milvus.py +17 -10
  7. vectordb_bench/backend/clients/pgvector/config.py +49 -0
  8. vectordb_bench/backend/clients/pgvector/pgvector.py +171 -0
  9. vectordb_bench/backend/clients/pinecone/pinecone.py +4 -3
  10. vectordb_bench/backend/clients/qdrant_cloud/config.py +20 -2
  11. vectordb_bench/backend/clients/qdrant_cloud/qdrant_cloud.py +11 -11
  12. vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py +5 -5
  13. vectordb_bench/backend/clients/zilliz_cloud/zilliz_cloud.py +3 -1
  14. vectordb_bench/backend/dataset.py +99 -149
  15. vectordb_bench/backend/result_collector.py +2 -2
  16. vectordb_bench/backend/runner/mp_runner.py +29 -13
  17. vectordb_bench/backend/runner/serial_runner.py +69 -51
  18. vectordb_bench/backend/task_runner.py +43 -48
  19. vectordb_bench/frontend/components/get_results/saveAsImage.py +4 -2
  20. vectordb_bench/frontend/const/dbCaseConfigs.py +35 -4
  21. vectordb_bench/frontend/const/dbPrices.py +5 -33
  22. vectordb_bench/frontend/const/styles.py +9 -3
  23. vectordb_bench/metric.py +0 -1
  24. vectordb_bench/models.py +12 -8
  25. vectordb_bench/results/dbPrices.json +32 -0
  26. vectordb_bench/results/getLeaderboardData.py +52 -0
  27. vectordb_bench/results/leaderboard.json +1 -0
  28. vectordb_bench/results/{result_20230609_standard.json → result_20230705_standard.json} +670 -214
  29. {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/METADATA +98 -13
  30. {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/RECORD +34 -29
  31. {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/LICENSE +0 -0
  32. {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/WHEEL +0 -0
  33. {vectordb_bench-0.0.2.dist-info → vectordb_bench-0.0.3.dist-info}/entry_points.txt +0 -0
  34. {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, train_emb: list[list[float]], train_id: list[int]):
26
- log.debug(f"Dataset shape: {len(train_emb)}")
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.shared_emb = train_emb
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 insert_data(self, left_id: int = 0) -> int:
32
+ def task(self) -> int:
33
+ count = 0
34
34
  with self.db.init():
35
- all_embeddings = self.shared_emb
36
-
37
- # unique id for endlessness insertion
38
- all_metadata = [i+left_id for i in self.train_id]
39
-
40
- num_conc_batches = math.ceil(len(all_embeddings)/NUM_PER_BATCH)
41
- log.info(f"({mp.current_process().name:16}) Start inserting {len(all_embeddings)} embeddings in batch {NUM_PER_BATCH}")
42
- count = 0
43
- for batch_id in range(self.seq_batches):
44
- metadata = all_metadata[batch_id*NUM_PER_BATCH: (batch_id+1)*NUM_PER_BATCH]
45
- embeddings = all_embeddings[batch_id*NUM_PER_BATCH: (batch_id+1)*NUM_PER_BATCH]
46
-
47
- log.debug(f"({mp.current_process().name:16}) batch [{batch_id:3}/{num_conc_batches}], Start inserting {len(metadata)} embeddings")
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=embeddings,
50
- metadata=metadata,
51
+ embeddings=all_embeddings,
52
+ metadata=all_metadata,
53
+ last_batch=last_batch,
51
54
  )
52
- if error != None:
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(metadata)
58
+ assert insert_count == len(all_metadata)
57
59
  count += insert_count
58
- log.info(f"({mp.current_process().name:16}) Finish inserting {len(all_embeddings)} embeddings in batch {NUM_PER_BATCH}")
59
- return count
60
+ if count % 100_000 == 0:
61
+ log.info(f"({mp.current_process().name:16}) Loaded {count} embeddings into VectorDB")
60
62
 
61
- def endless_insert_data(self, left_id: int = 0) -> int:
62
- with self.db.init():
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 self.train_id]
69
+ all_metadata = [i+left_id for i in all_metadata]
67
70
 
68
- num_conc_batches = math.ceil(len(all_embeddings)/NUM_PER_BATCH)
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(self.seq_batches):
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}/{num_conc_batches}], Start inserting {len(metadata)} embeddings")
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 != None:
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}/{num_conc_batches}], Finish inserting {len(metadata)} embeddings")
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.insert_data)
106
- count = future.result()
107
- return count
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 < config.CASE_TIMEOUT_IN_SECOND:
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.info(f"ground truth size: {ground_truth.columns}, shape: {ground_truth.shape}")
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._run_load_case()
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
- log.warning(f"unknown case type: {self.ca.label}")
100
- raise ValueError(f"Unknown case type: {self.ca.label}")
100
+ msg = f"unknown case type: {self.ca.label}"
101
+ log.warning(msg)
102
+ raise ValueError(msg)
101
103
 
102
-
103
- def _run_load_case(self) -> Metric:
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 error: {e}")
115
+ log.warning(f"Failed to run capacity case, reason = {e}")
122
116
  raise e from None
123
- log.info("End capacity case")
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 run error: {e}")
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
- for data_df in self.ca.dataset:
149
- try:
150
- all_metadata = data_df['id'].tolist()
151
-
152
- emb_np = np.stack(data_df['emb'])
153
- if self.normalize:
154
- log.debug("normalize the 100k train data")
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.ready_to_search()
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 ready_to_search error: {e}")
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.innerText = 'Creating Image...';
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.innerText = '{buttonText}';
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.backend.clients import DB
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/elasticsearch_beffeadc29.png",
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: "#fdc613",
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
@@ -19,7 +19,6 @@ class Metric:
19
19
  qps: float = 0.0
20
20
  serial_latency_p99: float = 0.0
21
21
  recall: float = 0.0
22
- quries_per_dollar: float = 0.0
23
22
 
24
23
 
25
24
  QURIES_PER_DOLLAR_METRIC = "QP$ (Quries per Dollar)"
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 == 0 else max_db_labels
171
- max_load_dur = 11 if max_load_dur == 0 else max_load_dur + 3
172
- max_qps = 10 if max_qps == 0 else max_load_dur + 3
173
- max_recall = 13 if max_recall == 0 else max_recall + 3
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
+ }