|
4 | 4 | import os
|
5 | 5 | import glob
|
6 | 6 | import json
|
| 7 | +from typing import List, Dict, Any |
7 | 8 |
|
8 |
| -def collate_results(dataset, results_directory): |
9 |
| - """Collate results from the results directory into a markdown table.""" |
10 |
| - json_files_pattern = os.path.join(results_directory, "*.json") |
11 | 9 |
|
| 10 | +INSTANCE_TYPE = 'n4-highmem-16' |
| 11 | +RUN = "hnsw" |
| 12 | +IGNORE_FIRST_TEST = True |
| 13 | +EF_VALS = [64] |
| 14 | + |
| 15 | +def get_all_data_as_dict(results_directory: str) -> List[Dict[str,Any]]: |
12 | 16 | data = []
|
13 | 17 |
|
14 |
| - for file_path in glob.glob(json_files_pattern): |
| 18 | + for file_path in glob.glob(results_directory + "/*.json"): |
15 | 19 | with open(file_path, 'r') as file:
|
16 |
| - file_data = json.load(file) |
| 20 | + file_data = json.load(file)[(IGNORE_FIRST_TEST == True):] |
17 | 21 | data.extend(file_data)
|
18 | 22 |
|
19 |
| - filtered_data = [entry for entry in data if entry['dataset_file'] == dataset] |
20 |
| - sorted_data = sorted(filtered_data, key=lambda x: x['qps'], reverse=True) |
| 23 | + return data |
| 24 | + |
| 25 | + |
| 26 | +def filter_data(data: List[Dict[str,Any]], dataset: str, limit: int, sorted_by: str="qps") -> List[Dict[str,Any]]: |
| 27 | + def _filter_data(item: Dict) -> bool: |
| 28 | + if ( |
| 29 | + item['dataset_file'] == dataset |
| 30 | + and item['limit'] == limit |
| 31 | + and item['instance_type'] == INSTANCE_TYPE |
| 32 | + and item['run'] == RUN |
| 33 | + and item['ef'] in EF_VALS |
| 34 | + ): |
| 35 | + return True |
| 36 | + return False |
| 37 | + |
| 38 | + return sorted( |
| 39 | + [entry for entry in data if _filter_data(entry)], |
| 40 | + key=lambda x: x[sorted_by], |
| 41 | + reverse=True, |
| 42 | + ) |
| 43 | + |
| 44 | + |
| 45 | +def collate_results(data: List[Dict[str,Any]], out=None): |
| 46 | + """Collate results from the results directory into a markdown table.""" |
21 | 47 |
|
22 |
| - print("""| efConstruction | maxConnections | ef | **Recall** | **QPS** | Mean Latency | p99 Latency | Import time | |
23 |
| -| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |""") |
24 |
| - for entry in sorted_data: |
| 48 | + print("| efConstruction | maxConnections | ef | **Recall** | **QPS** | Mean Latency | p99 Latency | Import time |", file=out) |
| 49 | + print("| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |", file=out) |
| 50 | + for entry in data: |
25 | 51 | latencyms = "{:.2f}ms".format(entry['meanLatency'] * 1000)
|
26 | 52 | p99ms = "{:.2f}ms".format(entry['p99Latency'] * 1000)
|
27 | 53 | recallfmt = "{:.2f}%".format(entry['recall'] * 100)
|
28 |
| - importtimefmt = "{:.2f}s".format(entry['importTime']) |
| 54 | + importtimefmt = "{:.0f}s".format(entry['importTime']) |
29 | 55 | qpsfmt = "{:.0f}".format(entry['qps'])
|
30 |
| - print(f"| {entry['efConstruction']} | {entry['maxConnections']} | {entry['ef']} | **{recallfmt}** | **{qpsfmt}** | {latencyms} | {p99ms} | {importtimefmt} |") |
| 56 | + print(f"| {entry['efConstruction']} | {entry['maxConnections']} | {entry['ef']} | **{recallfmt}** | **{qpsfmt}** | {latencyms} | {p99ms} | {importtimefmt} |", file=out) |
| 57 | + |
| 58 | + |
| 59 | +def weaviate_io_results(results_directory: str): |
| 60 | + |
| 61 | + data = get_all_data_as_dict(results_directory) |
| 62 | + datasets = set() |
| 63 | + limits = set() |
| 64 | + |
| 65 | + for item in data: |
| 66 | + datasets.add(item["dataset_file"]) |
| 67 | + limits.add(item["limit"]) |
| 68 | + |
| 69 | + for dataset in datasets: |
| 70 | + with open(f"ann-{dataset.replace(".hdf5", ".mdx")}", mode="w") as file: |
| 71 | + print("import Tabs from '@theme/Tabs';", file=file) |
| 72 | + print("import TabItem from '@theme/TabItem';\n", file=file) |
| 73 | + print('<Tabs groupId="limits">', file=file) |
| 74 | + for limit in limits: |
| 75 | + print(f'<TabItem value="{limit}" label="Limit {limit}">\n', file=file) |
| 76 | + collate_results( |
| 77 | + data=filter_data(data, dataset, limit), |
| 78 | + out=file, |
| 79 | + ) |
| 80 | + print('\n</TabItem>', file=file) |
| 81 | + print('</Tabs>', file=file) |
| 82 | + |
31 | 83 |
|
32 | 84 | if __name__ == "__main__":
|
33 | 85 | parser = argparse.ArgumentParser(description="Collate ann results into markdown tables.")
|
34 |
| - parser.add_argument('-d', '--dataset', required=True, help="The dataset file to filter by.") |
| 86 | + parser.add_argument('-d', '--dataset', default="all", type=str, help="The dataset file to filter by. If the value is 'all' or not specified, it will be computed for all datasets.") |
35 | 87 | parser.add_argument('-r', '--results', default="./results", help="The directory containing benchmark results")
|
| 88 | + parser.add_argument('-l', '--limit', default=10, type=int, help="The number of results returned by the ANN to fiter by") |
36 | 89 | args = parser.parse_args()
|
37 | 90 |
|
38 |
| - collate_results(args.dataset, os.path.expanduser(args.results)) |
| 91 | + if args.dataset != "all": |
| 92 | + filtered_data = filter_data( |
| 93 | + data=get_all_data_as_dict(args.results), |
| 94 | + dataset=args.dataset, |
| 95 | + limit=args.limit, |
| 96 | + ) |
| 97 | + |
| 98 | + collate_results(filtered_data) |
| 99 | + else: |
| 100 | + weaviate_io_results(args.results) |
0 commit comments