From 2d975c96add8ed6b950abb4ea4661850b5fcd128 Mon Sep 17 00:00:00 2001 From: Heemin Kim Date: Thu, 18 Jan 2024 10:35:30 -0800 Subject: [PATCH] Add perf test for nested field Signed-off-by: Heemin Kim --- benchmarks/perf-tool/README.md | 50 ++++++ .../perf-tool/add-parent-doc-id-to-dataset.py | 25 ++- benchmarks/perf-tool/dataset/data-nested.hdf5 | Bin 0 -> 74496 bytes .../dataset/sift-128-euclidean-nested.hdf5 | Bin 0 -> 74496 bytes .../perf-tool/okpt/test/steps/factory.py | 6 +- benchmarks/perf-tool/okpt/test/steps/steps.py | 155 +++++++++++++++++- .../faiss-hnsw/nested/simple/index.json | 32 ++++ .../nested/simple/simple-nested-test.yml | 37 +++++ .../lucene-hnsw/nested/simple/index.json | 31 ++++ .../nested/simple/simple-nested-test.yml | 37 +++++ 10 files changed, 356 insertions(+), 17 deletions(-) create mode 100644 benchmarks/perf-tool/dataset/data-nested.hdf5 create mode 100644 benchmarks/perf-tool/dataset/sift-128-euclidean-nested.hdf5 create mode 100644 benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/index.json create mode 100644 benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/simple-nested-test.yml create mode 100644 benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/index.json create mode 100644 benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/simple-nested-test.yml diff --git a/benchmarks/perf-tool/README.md b/benchmarks/perf-tool/README.md index 2d84df819..52590d22b 100644 --- a/benchmarks/perf-tool/README.md +++ b/benchmarks/perf-tool/README.md @@ -270,6 +270,26 @@ Ingests a dataset of multiple context types into the cluster. | ----------- | ----------- | ----------- | | took | Total time to ingest the dataset into the index.| ms | +#### ingest_nested_field + +Ingests a dataset with nested field into the cluster. + +##### Parameters + +| Parameter Name | Description | Default | +| ----------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ----------- | +| index_name | Name of index to ingest into | No default | +| field_name | Name of field to ingest into | No default | +| dataset_path | Path to data-set | No default | +| attributes_dataset_name | Name of dataset with additional attributes inside the main dataset | No default | +| attribute_spec | Definition of attributes, format is: [{ name: [name_val], type: [type_val]}] Order is important and must match order of attributes column in dataset file. It should contains { name: 'parent_id', type: 'int'} | No default | + +##### Metrics + +| Metric Name | Description | Unit | +| ----------- | ----------- | ----------- | +| took | Total time to ingest the dataset into the index.| ms | + #### query Runs a set of queries against an index. @@ -330,6 +350,36 @@ Runs a set of queries with filter against an index. | recall@R | ratio of top R results from the ground truth neighbors that are in the K results returned by the plugin | float 0.0-1.0 | | recall@K | ratio of results returned that were ground truth nearest neighbors | float 0.0-1.0 | + +#### query_nested_field + +Runs a set of queries with nested field against an index. + +##### Parameters + +| Parameter Name | Description | Default | +| ----------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| k | Number of neighbors to return on search | 100 | +| r | r value in Recall@R | 1 | +| index_name | Name of index to search | No default | +| field_name | Name field to search | No default | +| calculate_recall | Whether to calculate recall values | False | +| dataset_format | Format the dataset is in. Currently hdf5 and bigann is supported. The hdf5 file must be organized in the same way that the ann-benchmarks organizes theirs. | 'hdf5' | +| dataset_path | Path to dataset | No default | +| neighbors_format | Format the neighbors dataset is in. Currently hdf5 and bigann is supported. The hdf5 file must be organized in the same way that the ann-benchmarks organizes theirs. | 'hdf5' | +| neighbors_path | Path to neighbors dataset | No default | +| neighbors_dataset | Name of filter dataset inside the neighbors dataset | No default | +| query_count | Number of queries to create from data-set | Size of the data-set | + +##### Metrics + +| Metric Name | Description | Unit | +| ----------- | ----------- | ----------- | +| took | Took times returned per query aggregated as total, p50, p90 and p99 (when applicable) | ms | +| memory_kb | Native memory k-NN is using at the end of the query workload | KB | +| recall@R | ratio of top R results from the ground truth neighbors that are in the K results returned by the plugin | float 0.0-1.0 | +| recall@K | ratio of results returned that were ground truth nearest neighbors | float 0.0-1.0 | + #### get_stats Gets the index stats. diff --git a/benchmarks/perf-tool/add-parent-doc-id-to-dataset.py b/benchmarks/perf-tool/add-parent-doc-id-to-dataset.py index aed838f6b..54a40b281 100644 --- a/benchmarks/perf-tool/add-parent-doc-id-to-dataset.py +++ b/benchmarks/perf-tool/add-parent-doc-id-to-dataset.py @@ -152,18 +152,18 @@ def run(self, source_path, target_path) -> None: cpus = multiprocessing.cpu_count() total_clients = min(8, cpus) # 1 # 10 hdf5Data_train = HDF5DataSet(target_path, "train") - train_vectors = hdf5Data_train.read(0, 1000000) + train_vectors = hdf5Data_train.read(0, hdf5Data_train.size()) hdf5Data_train.close() print(f'Train vector size: {len(train_vectors)}') hdf5Data_test = HDF5DataSet(target_path, "test") - total_queries = 10000 # 10000 + total_queries = hdf5Data_test.size() # 10000 dis = [] * total_queries for i in range(total_queries): dis.insert(i, []) - queries_per_client = int(total_queries / total_clients) + queries_per_client = int(total_queries / total_clients + 0.5) if queries_per_client == 0: queries_per_client = total_queries @@ -176,7 +176,7 @@ def run(self, source_path, target_path) -> None: if start_index + queries_per_client <= total_queries: end_index = int(start_index + queries_per_client) else: - end_index = total_queries - start_index + end_index = total_queries print(f'Start Index: {start_index}, end Index: {end_index}') print(f'client is : {client}') @@ -199,7 +199,6 @@ def run(self, source_path, target_path) -> None: i = 0 for d in calculatedDis: if d: - print("Dis is not null") dis[i] = d j = j + 1 i = i + 1 @@ -238,14 +237,12 @@ def queryTask(train_vectors, test_vectors, startIndex, endIndex, process_number, i = startIndex for test in test_vectors: distances = [] - parent_ids = {} values = {} for value in train_vectors: - parent_ids[value.id] = value.parent_id values[value.id] = value distances.append({ "dis": calculateL2Distance(test.vector, value.vector), - "id": value.id + "id": value.parent_id }) distances.sort(key=lambda vector: vector['dis']) @@ -258,15 +255,15 @@ def queryTask(train_vectors, test_vectors, startIndex, endIndex, process_number, for sub_i in range(len(distances)): id = distances[sub_i]['id'] # Check if the number has been seen before - if len(nested) < 1000 and parent_ids[id] not in seen_set_nested: + if len(nested) < 1000 and id not in seen_set_nested: # If not seen before, mark it as seen - seen_set_nested.add(parent_ids[id]) + seen_set_nested.add(id) nested.append(distances[sub_i]) - if len(restricted) < 1000 and parent_ids[id] not in seen_set_restricted and values[id].apply_restricted_filter(): - seen_set_restricted.add(parent_ids[id]) + if len(restricted) < 1000 and id not in seen_set_restricted and values[id].apply_restricted_filter(): + seen_set_restricted.add(id) restricted.append(distances[sub_i]) - if len(relaxed) < 1000 and parent_ids[id] not in seen_set_relaxed and values[id].apply_relaxed_filter(): - seen_set_relaxed.add(parent_ids[id]) + if len(relaxed) < 1000 and id not in seen_set_relaxed and values[id].apply_relaxed_filter(): + seen_set_relaxed.add(id) relaxed.append(distances[sub_i]) all_distances[i]['nested'] = nested diff --git a/benchmarks/perf-tool/dataset/data-nested.hdf5 b/benchmarks/perf-tool/dataset/data-nested.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..4223d72810785f11ba801ac0fe819b0a72d0ada6 GIT binary patch literal 74496 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 Step: @@ -30,10 +30,14 @@ def create_step(step_config: StepConfig) -> Step: return IngestStep(step_config) elif step_config.step_name == IngestMultiFieldStep.label: return IngestMultiFieldStep(step_config) + elif step_config.step_name == IngestNestedFieldStep.label: + return IngestNestedFieldStep(step_config) elif step_config.step_name == QueryStep.label: return QueryStep(step_config) elif step_config.step_name == QueryWithFilterStep.label: return QueryWithFilterStep(step_config) + elif step_config.step_name == QueryNestedFieldStep.label: + return QueryNestedFieldStep(step_config) elif step_config.step_name == ForceMergeStep.label: return ForceMergeStep(step_config) elif step_config.step_name == ClearCacheStep.label: diff --git a/benchmarks/perf-tool/okpt/test/steps/steps.py b/benchmarks/perf-tool/okpt/test/steps/steps.py index 6ec164d9d..c0adba62a 100644 --- a/benchmarks/perf-tool/okpt/test/steps/steps.py +++ b/benchmarks/perf-tool/okpt/test/steps/steps.py @@ -333,7 +333,6 @@ def action(doc_id): for i in range(0, self.doc_count, self.bulk_size): partition = self.dataset.read(self.bulk_size) self._handle_data_bulk(partition, action, i) - self.dataset.reset() return {} @@ -379,6 +378,7 @@ def __init__(self, step_config: StepConfig): step_config.config, {}, []) self.partition_attr = self.attributes_dataset.read(self.doc_count) + self.action_buffer = None def _handle_data_bulk(self, partition, action, i): if partition is None: @@ -429,6 +429,118 @@ def bulk_transform_with_attributes(self, partition: np.ndarray, partition_attr, return actions +class IngestNestedFieldStep(BaseIngestStep): + """See base class.""" + + label = 'ingest_nested_field' + + def __init__(self, step_config: StepConfig): + super().__init__(step_config) + + dataset_path = parse_string_param('dataset_path', step_config.config, + {}, None) + + self.attributes_dataset_name = parse_string_param('attributes_dataset_name', + step_config.config, {}, None) + + self.attributes_dataset = parse_dataset('hdf5', dataset_path, + Context.CUSTOM, self.attributes_dataset_name) + + self.attribute_spec = parse_list_param('attribute_spec', + step_config.config, {}, []) + + self.partition_attr = self.attributes_dataset.read(self.doc_count) + + if self.dataset.size() != self.doc_count: + raise ValueError("custom doc_count is not supported for nested field") + self.action_buffer = None + self.action_parent_id = None + self.count = 0 + + def _handle_data_bulk(self, partition, action, i): + if partition is None: + return + body = self.bulk_transform_with_nested(partition, self.partition_attr, self.field_name, + action, i, self.attribute_spec) + if len(body) > 0: + bulk_index(self.opensearch, self.index_name, body) + + def bulk_transform_with_nested(self, partition: np.ndarray, partition_attr, field_name: str, + action, offset: int, attributes_def) -> List[Dict[str, Any]]: + """Partitions and transforms a list of vectors into OpenSearch's bulk + injection format. + Args: + partition: An array of vectors to transform. + partition_attr: dictionary of additional data to transform + field_name: field name for action + action: Bulk API action. + offset: to start counting from + attributes_def: definition of additional doc fields + Returns: + An array of transformed vectors in bulk format. + """ + # offset is index of start row. We need number of parent doc - 1. + # The number of parent document can be calculated by using partition_attr data. + # We need to keep the last parent doc aside so that additional data can be added later. + parent_id_idx = next((index for (index, d) in enumerate(attributes_def) if d.get('name') == 'parent_id'), None) + if parent_id_idx is None: + raise ValueError("parent_id should be provided as attribute spec") + if attributes_def[parent_id_idx]['type'] != 'int': + raise ValueError("parent_id should be int type") + + first_index = offset + last_index = offset + len(partition) - 1 + num_of_actions = int(partition_attr[last_index][parent_id_idx].decode()) - int(partition_attr[first_index][parent_id_idx].decode()) + if self.action_buffer is None: + self.action_buffer = {"nested_field": []} + self.action_parent_id = int(partition_attr[first_index][parent_id_idx].decode()) + + actions = [] + _ = [ + actions.extend([action(i + self.action_parent_id), None]) + for i in range(num_of_actions) + ] + + idx = 1 + part_list = partition.tolist() + for i in range(len(partition)): + self.count += 1 + nested = {field_name: part_list[i]} + attr_idx = i + offset + attr_def_idx = 0 + current_parent_id = None + for attribute in attributes_def: + attr_def_name = attribute['name'] + attr_def_type = attribute['type'] + if attr_def_name == "parent_id": + current_parent_id = int(partition_attr[attr_idx][attr_def_idx].decode()) + attr_def_idx += 1 + continue + + if attr_def_type == 'str': + val = partition_attr[attr_idx][attr_def_idx].decode() + if val != 'None': + nested[attr_def_name] = val + elif attr_def_type == 'int': + val = int(partition_attr[attr_idx][attr_def_idx].decode()) + nested[attr_def_name] = val + attr_def_idx += 1 + + if self.action_parent_id == current_parent_id: + self.action_buffer["nested_field"].append(nested) + else: + actions.extend([action(self.action_parent_id), self.action_buffer]) + self.action_buffer = {"nested_field": []} + self.action_buffer["nested_field"].append(nested) + self.action_parent_id = current_parent_id + idx += 2 + + if self.count == self.doc_count: + actions.extend([action(self.action_parent_id), self.action_buffer]) + + return actions + + class BaseQueryStep(OpenSearchStep): """See base class.""" @@ -469,7 +581,7 @@ def _action(self): break query_responses.append( query_index(self.opensearch, self.index_name, - self.get_body(query[0]) , [self.field_name])) + self.get_body(query[0]) , self.get_exclude_fields())) results['took'] = [ float(query_response['took']) for query_response in query_responses @@ -506,6 +618,8 @@ def _get_measures(self) -> List[str]: def get_body(self, vec): pass + def get_exclude_fields(self): + return [self.field_name] class QueryStep(BaseQueryStep): """See base class.""" @@ -611,6 +725,43 @@ def get_body(self, vec): else: raise ConfigurationError('Not supported filter type {}'.format(self.filter_type)) +class QueryNestedFieldStep(BaseQueryStep): + """See base class.""" + + label = 'query_nested_field' + + def __init__(self, step_config: StepConfig): + super().__init__(step_config) + + neighbors_dataset = parse_string_param('neighbors_dataset', + step_config.config, {}, None) + + self.neighbors = parse_dataset(self.neighbors_format, self.neighbors_path, + Context.CUSTOM, neighbors_dataset) + + self.implicit_config = step_config.implicit_config + + def get_body(self, vec): + return { + 'size': self.k, + 'query': { + 'nested': { + 'path': 'nested_field', + 'query': { + 'knn': { + 'nested_field.' + self.field_name: { + 'vector': vec, + 'k': self.k + } + } + } + } + } + } + + def get_exclude_fields(self): + return ['nested_field.' + self.field_name] + class GetStatsStep(OpenSearchStep): """See base class.""" diff --git a/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/index.json b/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/index.json new file mode 100644 index 000000000..a982afc81 --- /dev/null +++ b/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/index.json @@ -0,0 +1,32 @@ +{ + "settings": { + "index": { + "knn": true, + "number_of_shards": 24, + "number_of_replicas": 1, + "knn.algo_param.ef_search": 100 + } + }, + "mappings": { + "properties": { + "nested_field": { + "type": "nested", + "properties": { + "target_field": { + "type": "knn_vector", + "dimension": 128, + "method": { + "name": "hnsw", + "space_type": "l2", + "engine": "faiss", + "parameters": { + "ef_construction": 256, + "m": 16 + } + } + } + } + } + } + } +} diff --git a/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/simple-nested-test.yml b/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/simple-nested-test.yml new file mode 100644 index 000000000..151b2014d --- /dev/null +++ b/benchmarks/perf-tool/release-configs/faiss-hnsw/nested/simple/simple-nested-test.yml @@ -0,0 +1,37 @@ +endpoint: [ENDPOINT] +port: [PORT] +test_name: "Faiss HNSW Nested Field Test" +test_id: "Faiss HNSW Nested Field Test" +num_runs: 3 +show_runs: false +steps: + - name: delete_index + index_name: target_index + - name: create_index + index_name: target_index + index_spec: release-configs/faiss-hnsw/nested/simple/index.json + - name: ingest_nested_field + index_name: target_index + field_name: target_field + dataset_format: hdf5 + dataset_path: dataset/sift-128-euclidean-nested.hdf5 + attributes_dataset_name: attributes + attribute_spec: [ { name: 'color', type: 'str' }, { name: 'taste', type: 'str' }, { name: 'age', type: 'int' }, { name: 'parent_id', type: 'int'} ] + - name: refresh_index + index_name: target_index + - name: force_merge + index_name: target_index + max_num_segments: 1 + - name: warmup_operation + index_name: target_index + - name: query_nested_field + k: 100 + r: 1 + calculate_recall: true + index_name: target_index + field_name: target_field + dataset_format: hdf5 + dataset_path: dataset/sift-128-euclidean-nested.hdf5 + neighbors_format: hdf5 + neighbors_path: dataset/sift-128-euclidean-nested.hdf5 + neighbors_dataset: neighbour_nested \ No newline at end of file diff --git a/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/index.json b/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/index.json new file mode 100644 index 000000000..8dc749c39 --- /dev/null +++ b/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/index.json @@ -0,0 +1,31 @@ +{ + "settings": { + "index": { + "knn": true, + "number_of_shards": 24, + "number_of_replicas": 1 + } + }, + "mappings": { + "properties": { + "nested_field": { + "type": "nested", + "properties": { + "target_field": { + "type": "knn_vector", + "dimension": 128, + "method": { + "name": "hnsw", + "space_type": "l2", + "engine": "lucene", + "parameters": { + "ef_construction": 256, + "m": 16 + } + } + } + } + } + } + } +} diff --git a/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/simple-nested-test.yml b/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/simple-nested-test.yml new file mode 100644 index 000000000..cf1e4edc4 --- /dev/null +++ b/benchmarks/perf-tool/release-configs/lucene-hnsw/nested/simple/simple-nested-test.yml @@ -0,0 +1,37 @@ +endpoint: [ENDPOINT] +port: [PORT] +test_name: "Lucene HNSW Nested Field Test" +test_id: "Lucene HNSW Nested Field Test" +num_runs: 3 +show_runs: false +steps: + - name: delete_index + index_name: target_index + - name: create_index + index_name: target_index + index_spec: release-configs/faiss-hnsw/nested/simple/index.json + - name: ingest_nested_field + index_name: target_index + field_name: target_field + dataset_format: hdf5 + dataset_path: dataset/sift-128-euclidean-nested.hdf5 + attributes_dataset_name: attributes + attribute_spec: [ { name: 'color', type: 'str' }, { name: 'taste', type: 'str' }, { name: 'age', type: 'int' }, { name: 'parent_id', type: 'int'} ] + - name: refresh_index + index_name: target_index + - name: force_merge + index_name: target_index + max_num_segments: 1 + - name: warmup_operation + index_name: target_index + - name: query_nested_field + k: 100 + r: 1 + calculate_recall: true + index_name: target_index + field_name: target_field + dataset_format: hdf5 + dataset_path: dataset/sift-128-euclidean-nested.hdf5 + neighbors_format: hdf5 + neighbors_path: dataset/sift-128-euclidean-nested.hdf5 + neighbors_dataset: neighbour_nested \ No newline at end of file