-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbenchmark_bfs.py
More file actions
70 lines (53 loc) · 2.39 KB
/
Copy pathbenchmark_bfs.py
File metadata and controls
70 lines (53 loc) · 2.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""Benchmark the workspace-reused BFS path (``lifted_edges_from_node_labels``).
The code review changed ``BfsWorkspace::reset`` from an O(N) clear of the
visited/distance buffers to an O(1) generation-stamp bump. That only pays off
when one workspace is reset across many sources, which is exactly what
``lifted_edges_from_node_labels`` does (one workspace per chunk, reset per
source). The single-call ``breadth_first_search`` builds a fresh workspace each
call and would NOT show the change, so we benchmark the lifted-edge path.
Single-threaded so the per-source reset cost is not hidden by parallelism. The
node count is swept to expose the previous O(N^2)-of-memset behavior.
Not part of the test suite. Run::
python development/graph/benchmark_bfs.py --repeats 5
"""
from __future__ import annotations
import argparse
from statistics import median
from time import perf_counter
import numpy as np
import bioimage_cpp as bic
def _timeit(fn, repeats: int, warmup: int = 1) -> dict:
for _ in range(warmup):
fn()
timings = []
for _ in range(repeats):
t0 = perf_counter()
fn()
timings.append(perf_counter() - t0)
return {"median": median(timings), "min": min(timings), "n": repeats}
def run(repeats: int = 5, depth: int = 2) -> dict:
shapes = [(100, 100), (160, 160), (220, 220)]
results: dict[str, dict] = {}
for shape in shapes:
n_nodes = int(np.prod(shape))
graph = bic.graph.grid_graph(shape)
rng = np.random.default_rng(0)
node_labels = rng.integers(0, 50, size=(n_nodes,), dtype=np.uint64)
def fn(g=graph, nl=node_labels, d=depth):
bic.graph.lifted_multicut.lifted_edges_from_node_labels(
g, nl, graph_depth=d, number_of_threads=1
)
key = f"bfs_lifted_{n_nodes}nodes_d{depth}"
results[key] = _timeit(fn, repeats)
results[key]["meta"] = {"n_nodes": n_nodes, "shape": shape, "depth": depth}
return results
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repeats", type=int, default=5)
parser.add_argument("--depth", type=int, default=2)
args = parser.parse_args()
results = run(repeats=args.repeats, depth=args.depth)
for name, r in results.items():
print(f"{name:<28} median={r['median'] * 1e3:9.3f} ms min={r['min'] * 1e3:9.3f} ms")
if __name__ == "__main__":
main()