Skip to content

Commit 4af9ce8

Browse files
committed
Add ASV benchmarks for 6 uncovered modules (#1137)
normalize, diffusion, erosion, balanced_allocation, dasymetric, and reproject all received memory-guard or lazy-reduction fixes in the v0.9.5 cycle but had no ASV benchmark coverage. Each new module follows the existing Benchmarking base-class pattern with appropriate grid sizes and backend params.
1 parent 45c714e commit 4af9ce8

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import numpy as np
2+
import xarray as xr
3+
4+
from xrspatial.balanced_allocation import balanced_allocation
5+
6+
from .common import get_xr_dataarray
7+
8+
9+
class BalancedAllocation:
10+
# Memory-intensive: holds N_sources cost surfaces simultaneously.
11+
# Keep grids small to avoid OOM during benchmarking.
12+
params = ([100, 300], ["numpy", "dask"])
13+
param_names = ("nx", "type")
14+
15+
def setup(self, nx, type):
16+
ny = nx // 2
17+
# Friction surface: positive values everywhere.
18+
friction = get_xr_dataarray((ny, nx), type)
19+
if type == "dask":
20+
import dask.array as da
21+
friction.data = da.fabs(friction.data) + 0.1
22+
else:
23+
friction.data = np.abs(friction.data) + 0.1
24+
self.friction = friction
25+
26+
# Source raster: place 4 source points in corners.
27+
sources = np.zeros((ny, nx), dtype=np.float32)
28+
margin = max(1, nx // 10)
29+
sources[margin, margin] = 1
30+
sources[margin, nx - margin - 1] = 2
31+
sources[ny - margin - 1, margin] = 3
32+
sources[ny - margin - 1, nx - margin - 1] = 4
33+
34+
x = np.linspace(-180, 180, nx)
35+
y = np.linspace(-90, 90, ny)
36+
37+
if type == "dask":
38+
import dask.array as da
39+
data = da.from_array(sources, chunks=(max(1, ny // 2), max(1, nx // 2)))
40+
else:
41+
data = sources
42+
43+
self.raster = xr.DataArray(data, coords=dict(y=y, x=x), dims=["y", "x"])
44+
45+
def time_balanced_allocation(self, nx, type):
46+
balanced_allocation(self.raster, self.friction, max_iterations=10)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import numpy as np
2+
import xarray as xr
3+
4+
from xrspatial.dasymetric import disaggregate
5+
6+
from .common import get_xr_dataarray
7+
8+
9+
class Dasymetric:
10+
params = ([100, 300, 1000], ["numpy", "cupy", "dask"])
11+
param_names = ("nx", "type")
12+
13+
def setup(self, nx, type):
14+
ny = nx // 2
15+
16+
# Zones: 4 rectangular blocks.
17+
zones_np = np.zeros((ny, nx), dtype=np.int32)
18+
zones_np[: ny // 2, : nx // 2] = 1
19+
zones_np[: ny // 2, nx // 2 :] = 2
20+
zones_np[ny // 2 :, : nx // 2] = 3
21+
zones_np[ny // 2 :, nx // 2 :] = 4
22+
23+
x = np.linspace(-180, 180, nx)
24+
y = np.linspace(-90, 90, ny)
25+
26+
if type == "dask":
27+
import dask.array as da
28+
zdata = da.from_array(zones_np, chunks=(max(1, ny // 2), max(1, nx // 2)))
29+
elif type == "cupy":
30+
from xrspatial.utils import has_cuda_and_cupy
31+
if not has_cuda_and_cupy:
32+
raise NotImplementedError()
33+
import cupy
34+
zdata = cupy.asarray(zones_np)
35+
else:
36+
zdata = zones_np
37+
38+
self.zones = xr.DataArray(zdata, coords=dict(y=y, x=x), dims=["y", "x"])
39+
40+
# Values: one total per zone.
41+
self.values = {1: 1000.0, 2: 2000.0, 3: 1500.0, 4: 2500.0}
42+
43+
# Weight surface: use the standard Gaussian bump.
44+
weight = get_xr_dataarray((ny, nx), type)
45+
# Make weights non-negative.
46+
if type == "dask":
47+
import dask.array as da
48+
weight.data = da.fabs(weight.data) + 0.01
49+
elif type == "cupy":
50+
import cupy
51+
weight.data = cupy.abs(weight.data) + 0.01
52+
else:
53+
weight.data = np.abs(weight.data) + 0.01
54+
self.weight = weight
55+
56+
def time_disaggregate_weighted(self, nx, type):
57+
disaggregate(self.zones, self.values, self.weight, method="weighted")
58+
59+
def time_disaggregate_binary(self, nx, type):
60+
disaggregate(self.zones, self.values, self.weight, method="binary")

benchmarks/benchmarks/diffusion.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from xrspatial.diffusion import diffuse
2+
3+
from .common import Benchmarking
4+
5+
6+
class Diffusion(Benchmarking):
7+
params = ([100, 300, 1000], ["numpy", "cupy", "dask"])
8+
param_names = ("nx", "type")
9+
10+
def __init__(self):
11+
super().__init__(func=None)
12+
13+
def time_diffuse_1step(self, nx, type):
14+
diffuse(self.xr, diffusivity=1.0, steps=1)
15+
16+
def time_diffuse_10steps(self, nx, type):
17+
diffuse(self.xr, diffusivity=1.0, steps=10)

benchmarks/benchmarks/erosion.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from xrspatial.erosion import erode
2+
3+
from .common import get_xr_dataarray
4+
5+
6+
class Erosion:
7+
# Erosion is a global operation that materialises dask arrays.
8+
# Keep grid sizes small and iteration counts low so benchmarks
9+
# finish in reasonable time.
10+
params = ([100, 300], ["numpy", "cupy", "dask"])
11+
param_names = ("nx", "type")
12+
13+
def setup(self, nx, type):
14+
ny = nx // 2
15+
self.xr = get_xr_dataarray((ny, nx), type)
16+
17+
def time_erode_500(self, nx, type):
18+
erode(self.xr, iterations=500, seed=42)
19+
20+
def time_erode_5000(self, nx, type):
21+
erode(self.xr, iterations=5000, seed=42)

benchmarks/benchmarks/normalize.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from xrspatial.normalize import rescale, standardize
2+
3+
from .common import Benchmarking
4+
5+
6+
class Rescale(Benchmarking):
7+
def __init__(self):
8+
super().__init__(func=rescale)
9+
10+
def time_rescale(self, nx, type):
11+
return self.time(nx, type)
12+
13+
14+
class Standardize(Benchmarking):
15+
def __init__(self):
16+
super().__init__(func=standardize)
17+
18+
def time_standardize(self, nx, type):
19+
return self.time(nx, type)

benchmarks/benchmarks/reproject.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import numpy as np
2+
import xarray as xr
3+
4+
from .common import get_xr_dataarray
5+
6+
7+
def _has_pyproj():
8+
try:
9+
import pyproj # noqa: F401
10+
return True
11+
except ImportError:
12+
return False
13+
14+
15+
class Reproject:
16+
params = ([100, 300, 1000], ["numpy", "dask"])
17+
param_names = ("nx", "type")
18+
19+
def setup(self, nx, type):
20+
if not _has_pyproj():
21+
raise NotImplementedError("pyproj required")
22+
23+
ny = nx // 2
24+
self.xr = get_xr_dataarray((ny, nx), type)
25+
# Tag with WGS84 so reproject knows the source CRS.
26+
self.xr.attrs["crs"] = "EPSG:4326"
27+
28+
def time_reproject_to_mercator(self, nx, type):
29+
from xrspatial.reproject import reproject
30+
reproject(self.xr, "EPSG:3857")
31+
32+
def time_reproject_to_utm(self, nx, type):
33+
from xrspatial.reproject import reproject
34+
reproject(self.xr, "EPSG:32610")

0 commit comments

Comments
 (0)