diff --git a/.claude/sweep-benchmarks-state.csv b/.claude/sweep-benchmarks-state.csv index 17581263b..a60a16744 100644 --- a/.claude/sweep-benchmarks-state.csv +++ b/.claude/sweep-benchmarks-state.csv @@ -1,2 +1,3 @@ module,last_inspected,issue,severity_max,categories_found,notes geotiff,2026-07-02,3603,HIGH,1;2,"No benchmark existed for geotiff; open_geotiff/to_geotiff had zero asv coverage across numpy/dask/cupy. Added benchmarks/benchmarks/geotiff.py: WriteGeoTIFF (numpy/dask/cupy streaming), WriteCOG (numpy/cupy overview pyramid), ReadGeoTIFF (numpy/cupy decode), ReadGeoTIFFChunked (dask). All classes executed locally via direct call; cupy paths run on this GPU host. asv check discover fails suite-wide from an asv_runner + py3.14 metadata bug, unrelated to this file." +pathfinding,2026-07-08,3645,HIGH,1;2;3,"Bench covered only numpy a_star_search at nx<=300; module also ships dask (separate sparse-Python A* + LRU chunk cache), cupy fallback, and public multi_stop_search with zero coverage. Extended AStarSearch to numpy/cupy/dask with nx up to 1000 (dask capped at 300, ~4s/call at 1000) and added MultiStopSearch (ordered + optimize_order). All combos executed locally incl. cupy (GPU host). LOW noted, not fixed: open-grid no-barrier/no-friction input is A* best case. dask+cupy not parameterized anywhere in suite (common.get_xr_dataarray has no such type). Existing bench imports/runs fine (Cat 4 clean)." diff --git a/benchmarks/benchmarks/pathfinding.py b/benchmarks/benchmarks/pathfinding.py index 0207c06b2..fac942c1d 100644 --- a/benchmarks/benchmarks/pathfinding.py +++ b/benchmarks/benchmarks/pathfinding.py @@ -1,21 +1,52 @@ -from xrspatial.pathfinding import a_star_search +from xrspatial.pathfinding import a_star_search, multi_stop_search from .common import get_xr_dataarray class AStarSearch: - params = ([10, 100, 300], [4, 8], ["numpy"]) + params = ([100, 300, 1000], [4, 8], ["numpy", "cupy", "dask"]) param_names = ("nx", "connectivity", "type") def setup(self, nx, connectivity, type): + if type == "dask" and nx > 300: + # The dask backend is a pure-Python sparse A* that loads + # chunks on demand; at nx=1000 a single call takes ~4 s, + # which would dominate the suite's runtime. + raise NotImplementedError() ny = nx // 2 self.agg = get_xr_dataarray((ny, nx), type) self.start = self.agg.y[0], self.agg.x[0] self.goal = self.agg.y[-1], self.agg.x[-1] + # snap_start/snap_goal raise on dask-backed arrays by design + self.snap = type != "dask" def time_a_star_search(self, nx, connectivity, type): a_star_search( self.agg, self.start, self.goal, connectivity=connectivity, - snap_start=True, snap_goal=True + snap_start=self.snap, snap_goal=self.snap ) + + +class MultiStopSearch: + params = ([100, 300], ["numpy", "cupy", "dask"]) + param_names = ("nx", "type") + + def setup(self, nx, type): + ny = nx // 2 + self.agg = get_xr_dataarray((ny, nx), type) + ys = self.agg.y.data + xs = self.agg.x.data + # 4 waypoints zigzagging across the grid (3 segments) + self.waypoints = [ + (ys[0], xs[0]), + (ys[-1], xs[nx // 3]), + (ys[0], xs[2 * nx // 3]), + (ys[-1], xs[-1]), + ] + + def time_multi_stop_search(self, nx, type): + multi_stop_search(self.agg, self.waypoints) + + def time_multi_stop_search_optimize_order(self, nx, type): + multi_stop_search(self.agg, self.waypoints, optimize_order=True)