| 101 | |
| 102 | |
| 103 | def benchmark_token_savior(name: str, root: Path) -> tuple[dict, list[str]]: |
| 104 | from token_savior.cache_ops import CacheManager |
| 105 | from token_savior.project_indexer import ProjectIndexer |
| 106 | from token_savior.query_api import create_project_query_functions |
| 107 | |
| 108 | root_str = str(root) |
| 109 | out: dict = {} |
| 110 | |
| 111 | log(f"[ts] {name}: cold index ...") |
| 112 | tracemalloc.start() |
| 113 | t0 = time.perf_counter() |
| 114 | indexer = ProjectIndexer(root_str) |
| 115 | index = indexer.index() |
| 116 | out["cold_index_seconds"] = round(time.perf_counter() - t0, 3) |
| 117 | out["cold_index_peak_memory_bytes"] = tracemalloc.get_traced_memory()[1] |
| 118 | tracemalloc.stop() |
| 119 | out["total_files"] = index.total_files |
| 120 | out["total_lines"] = index.total_lines |
| 121 | out["total_functions"] = index.total_functions |
| 122 | out["total_classes"] = index.total_classes |
| 123 | out["symbol_table_size"] = len(index.symbol_table) |
| 124 | |
| 125 | log(f"[ts] {name}: warm index ...") |
| 126 | t0 = time.perf_counter() |
| 127 | ProjectIndexer(root_str).index() |
| 128 | out["warm_index_seconds"] = round(time.perf_counter() - t0, 3) |
| 129 | |
| 130 | queries = create_project_query_functions(index) |
| 131 | rng = random.Random(RANDOM_SEED) |
| 132 | symbols = list(index.symbol_table.keys()) |
| 133 | sample = rng.sample(symbols, min(NUM_QUERY_SAMPLES, len(symbols))) |
| 134 | |
| 135 | def avg_ms(fn, items: list[str]) -> float | None: |
| 136 | times: list[float] = [] |
| 137 | for it in items: |
| 138 | t = time.perf_counter() |
| 139 | fn(it) |
| 140 | times.append(time.perf_counter() - t) |
| 141 | return round(sum(times) / len(times) * 1000, 3) if times else None |
| 142 | |
| 143 | out["find_symbol_avg_ms"] = avg_ms(queries["find_symbol"], sample) |
| 144 | out["get_function_source_avg_ms"] = avg_ms(queries["get_function_source"], sample) |
| 145 | impact_sample = [s for s in sample if s in index.reverse_dependency_graph] |
| 146 | out["get_change_impact_avg_ms"] = ( |
| 147 | avg_ms(queries["get_change_impact"], impact_sample) if impact_sample else None |
| 148 | ) |
| 149 | |
| 150 | cache = CacheManager(root_path=root_str, cache_version=1) |
| 151 | cache.save(index) |
| 152 | cp = cache.path() |
| 153 | out["cache_size_bytes"] = os.path.getsize(cp) if os.path.exists(cp) else 0 |
| 154 | try: |
| 155 | os.remove(cp) |
| 156 | except OSError: |
| 157 | pass |
| 158 | |
| 159 | return out, sample |
| 160 | |