Skip to content

Commit ecdceba

Browse files
committed
Add profile computation benchmark script
Measures wall time and peak memory for compute_profiles, compute_density_profile, and compute_speed_profile with both precomputed and on-the-fly intersection paths.
1 parent 4d037d4 commit ecdceba

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

scripts/bench_profiles.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env python3
2+
"""Benchmark profile computation: timing and peak memory.
3+
4+
Usage:
5+
python scripts/bench_profiles.py [--frames N] [--peds N] [--grid-size F] [--repeats N]
6+
7+
Run on both main and the feature branch to compare.
8+
"""
9+
10+
import argparse
11+
import time
12+
import tracemalloc
13+
14+
import numpy as np
15+
import pandas as pd
16+
import shapely
17+
18+
from pedpy.data.geometry import WalkableArea
19+
from pedpy.methods.profile_calculator import (
20+
DensityMethod,
21+
SpeedMethod,
22+
compute_density_profile,
23+
compute_grid_cell_polygon_intersection_area,
24+
compute_profiles,
25+
compute_speed_profile,
26+
get_grid_cells,
27+
)
28+
29+
30+
def generate_data(n_frames: int, n_peds: int, area_size: float = 10.0) -> pd.DataFrame:
31+
"""Generate synthetic pedestrian data with Voronoi-like polygons."""
32+
rng = np.random.default_rng(42)
33+
rows = []
34+
for frame in range(n_frames):
35+
xs = rng.uniform(0.5, area_size - 0.5, n_peds)
36+
ys = rng.uniform(0.5, area_size - 0.5, n_peds)
37+
speeds = rng.uniform(0.5, 2.0, n_peds)
38+
# Simple square polygons centered on each pedestrian
39+
cell_size = 0.4
40+
polys = [shapely.box(x - cell_size, y - cell_size, x + cell_size, y + cell_size) for x, y in zip(xs, ys)]
41+
for i in range(n_peds):
42+
rows.append(
43+
{
44+
"id": i,
45+
"frame": frame,
46+
"x": xs[i],
47+
"y": ys[i],
48+
"speed": speeds[i],
49+
"polygon": polys[i],
50+
}
51+
)
52+
return pd.DataFrame(rows)
53+
54+
55+
def bench(func, label, repeats=3):
56+
"""Run func, measure wall time and peak memory."""
57+
times = []
58+
peak_mem = 0
59+
for _ in range(repeats):
60+
tracemalloc.start()
61+
t0 = time.perf_counter()
62+
try:
63+
func()
64+
except Exception as e:
65+
tracemalloc.stop()
66+
print(f" {label:45s} SKIPPED ({type(e).__name__})")
67+
return
68+
elapsed = time.perf_counter() - t0
69+
_, peak = tracemalloc.get_traced_memory()
70+
tracemalloc.stop()
71+
times.append(elapsed)
72+
peak_mem = max(peak_mem, peak)
73+
74+
median_t = np.median(times)
75+
print(f" {label:45s} {median_t:7.3f}s peak_mem={peak_mem / 1024 / 1024:7.1f} MB")
76+
77+
78+
def main():
79+
parser = argparse.ArgumentParser(description="Benchmark profile computation")
80+
parser.add_argument("--frames", type=int, default=200, help="Number of frames")
81+
parser.add_argument("--peds", type=int, default=80, help="Pedestrians per frame")
82+
parser.add_argument("--grid-size", type=float, default=0.5, help="Grid cell size")
83+
parser.add_argument("--repeats", type=int, default=3, help="Timing repeats")
84+
args = parser.parse_args()
85+
86+
area_size = 10.0
87+
walkable_area = WalkableArea(shapely.box(0, 0, area_size, area_size))
88+
89+
print(f"Generating data: {args.frames} frames x {args.peds} peds = {args.frames * args.peds} rows")
90+
data = generate_data(args.frames, args.peds, area_size)
91+
print(f"Grid size: {args.grid_size} => {int(area_size / args.grid_size)}x{int(area_size / args.grid_size)} cells")
92+
print()
93+
94+
grid_cells, _, _ = get_grid_cells(walkable_area=walkable_area, grid_size=args.grid_size)
95+
96+
# --- With precomputed intersections ---
97+
print("Precomputing grid intersections...")
98+
t0 = time.perf_counter()
99+
precomputed, sorted_data = compute_grid_cell_polygon_intersection_area(data=data, grid_cells=grid_cells)
100+
print(f" Precomputation took {time.perf_counter() - t0:.3f}s")
101+
print()
102+
103+
print(f"Benchmarks (median of {args.repeats} runs):")
104+
105+
bench(
106+
lambda: compute_density_profile(
107+
data=sorted_data,
108+
walkable_area=walkable_area,
109+
grid_size=args.grid_size,
110+
density_method=DensityMethod.VORONOI,
111+
grid_intersections_area=precomputed,
112+
),
113+
"density_profile (voronoi, precomputed)",
114+
args.repeats,
115+
)
116+
117+
bench(
118+
lambda: compute_density_profile(
119+
data=data,
120+
walkable_area=walkable_area,
121+
grid_size=args.grid_size,
122+
density_method=DensityMethod.VORONOI,
123+
),
124+
"density_profile (voronoi, on-the-fly)",
125+
args.repeats,
126+
)
127+
128+
bench(
129+
lambda: compute_speed_profile(
130+
data=sorted_data,
131+
walkable_area=walkable_area,
132+
grid_size=args.grid_size,
133+
speed_method=SpeedMethod.VORONOI,
134+
grid_intersections_area=precomputed,
135+
),
136+
"speed_profile (voronoi, precomputed)",
137+
args.repeats,
138+
)
139+
140+
bench(
141+
lambda: compute_speed_profile(
142+
data=data,
143+
walkable_area=walkable_area,
144+
grid_size=args.grid_size,
145+
speed_method=SpeedMethod.VORONOI,
146+
),
147+
"speed_profile (voronoi, on-the-fly)",
148+
args.repeats,
149+
)
150+
151+
bench(
152+
lambda: compute_profiles(
153+
data=data,
154+
walkable_area=walkable_area,
155+
grid_size=args.grid_size,
156+
speed_method=SpeedMethod.VORONOI,
157+
),
158+
"compute_profiles (voronoi, on-the-fly)",
159+
args.repeats,
160+
)
161+
162+
163+
if __name__ == "__main__":
164+
main()

0 commit comments

Comments
 (0)