Skip to content
15 changes: 12 additions & 3 deletions benchmarks/benchmarks/ci/esmf_regridder.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,13 @@ def setup(self, type):
src = Cube(src_data)
src.add_dim_coord(grid.coord("latitude"), 0)
src.add_dim_coord(grid.coord("longitude"), 1)
self.regridder = ESMFAreaWeightedRegridder(src, tgt)
self.regrid_class = ESMFAreaWeightedRegridder
self.regridder = self.regrid_class(src, tgt)
self.src = src
self.tgt = tgt

def time_prepare_regridding(self, type):
_ = self.regrid_class(self.src, self.tgt)

def time_perform_regridding(self, type):
_ = self.regridder(self.src)
Expand Down Expand Up @@ -202,8 +207,10 @@ def setup(self, type):
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
src.add_aux_coord(mesh_coord_x, 0)
src.add_aux_coord(mesh_coord_y, 0)
self.regridder = MeshToGridESMFRegridder(src, tgt)
self.regrid_class = MeshToGridESMFRegridder
self.regridder = self.regrid_class(src, tgt)
self.src = src
self.tgt = tgt


@disable_repeat_between_setup
Expand Down Expand Up @@ -299,8 +306,10 @@ def setup(self, type):
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
tgt.add_aux_coord(mesh_coord_x, 0)
tgt.add_aux_coord(mesh_coord_y, 0)
self.regridder = GridToMeshESMFRegridder(src, tgt)
self.regrid_class = GridToMeshESMFRegridder
self.regridder = self.regrid_class(src, tgt)
self.src = src
self.tgt = tgt


@disable_repeat_between_setup
Expand Down
186 changes: 186 additions & 0 deletions benchmarks/benchmarks/long/esmf_regridder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
"""Slower benchmarks for :mod:`esmf_regrid.esmf_regridder`."""

from pathlib import Path

import numpy as np
import dask.array as da
import iris
from iris.cube import Cube

from benchmarks import disable_repeat_between_setup
from esmf_regrid.experimental.unstructured_scheme import (
GridToMeshESMFRegridder,
MeshToGridESMFRegridder,
)
from esmf_regrid.schemes import ESMFAreaWeightedRegridder
from esmf_regrid.tests.unit.schemes.test__cube_to_GridInfo import _grid_cube


class PrepareScalabilityGridToGrid:
params = [50, 100, 200, 400, 600, 800]
param_names = ["grid width"]
height = 100
regridder = ESMFAreaWeightedRegridder

def src_cube(self, n):
lon_bounds = (-180, 180)
lat_bounds = (-90, 90)
src = _grid_cube(n, n, lon_bounds, lat_bounds)
return src

def tgt_cube(self, n):
lon_bounds = (-180, 180)
lat_bounds = (-90, 90)
grid = _grid_cube(n + 1, n + 1, lon_bounds, lat_bounds)
return grid

def setup(self, n):
self.src = self.src_cube(n)
self.tgt = self.tgt_cube(n)

def time_prepare(self, n):
_ = self.regridder(self.src, self.tgt)


class PrepareScalabilityMeshToGrid(PrepareScalabilityGridToGrid):
regridder = MeshToGridESMFRegridder

def src_cube(self, n):
from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__mesh_to_MeshInfo import (
_gridlike_mesh,
)

src = Cube(np.ones([n * n]))
mesh = _gridlike_mesh(n, n)
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
src.add_aux_coord(mesh_coord_x, 0)
src.add_aux_coord(mesh_coord_y, 0)
return src


class PrepareScalabilityGridToMesh(PrepareScalabilityGridToGrid):
regridder = GridToMeshESMFRegridder

def tgt_cube(self, n):
from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__mesh_to_MeshInfo import (
_gridlike_mesh,
)

tgt = Cube(np.ones([(n + 1) * (n + 1)]))
mesh = _gridlike_mesh(n + 1, n + 1)
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
tgt.add_aux_coord(mesh_coord_x, 0)
tgt.add_aux_coord(mesh_coord_y, 0)
return tgt


@disable_repeat_between_setup
class PerformScalabilityGridToGrid:
params = [100, 200, 400, 600, 800, 1000]
param_names = ["height"]
grid_size = 400
target_grid_size = 41
chunk_size = [grid_size, grid_size, 10]
regridder = ESMFAreaWeightedRegridder
file_name = "chunked_cube.nc"

def src_cube(self, height):
data = da.ones([self.grid_size, self.grid_size, height], chunks=self.chunk_size)
src = Cube(data)
return src

def add_src_metadata(self, cube):
lon_bounds = (-180, 180)
lat_bounds = (-90, 90)
grid = _grid_cube(self.grid_size, self.grid_size, lon_bounds, lat_bounds)
cube.add_dim_coord(grid.coord("latitude"), 0)
cube.add_dim_coord(grid.coord("longitude"), 1)
return cube

def tgt_cube(self):
lon_bounds = (-180, 180)
lat_bounds = (-90, 90)
grid = _grid_cube(
self.target_grid_size, self.target_grid_size, lon_bounds, lat_bounds
)
return grid

def setup_cache(self):
SYNTH_DATA_DIR = Path().cwd() / "tmp_data"
SYNTH_DATA_DIR.mkdir(exist_ok=True)
file = str(SYNTH_DATA_DIR.joinpath(self.file_name))

src = self.src_cube(max(self.params))
# While iris is not able to save meshes, we add these after loading.
# TODO: change this back after iris allows mesh saving.
iris.save(src, file, chunksizes=self.chunk_size)
loaded_src = iris.load_cube(file)
loaded_src = self.add_src_metadata(loaded_src)
Comment thread
trexfeathers marked this conversation as resolved.
tgt = self.tgt_cube()
rg = self.regridder(loaded_src, tgt)
return rg, file

def setup(self, cache, height):
regridder, file = cache
src = iris.load_cube(file)[..., :height]
self.src = self.add_src_metadata(src)
# Realise data.
self.src.data
cube = iris.load_cube(file)[..., :height]
cube = self.add_src_metadata(cube)
self.result = regridder(cube)

def time_perform(self, cache, height):
assert not self.src.has_lazy_data()
rg, _ = cache
_ = rg(self.src)

def time_lazy_perform(self, cache, height):
assert self.result.has_lazy_data()
_ = self.result.data
Comment thread
trexfeathers marked this conversation as resolved.


class PerformScalabilityMeshToGrid(PerformScalabilityGridToGrid):
regridder = MeshToGridESMFRegridder
chunk_size = [PerformScalabilityGridToGrid.grid_size ^ 2, 10]
file_name = "chunked_cube_1d.nc"

def setup_cache(self):
return super().setup_cache()

def src_cube(self, height):
data = da.ones(
[self.grid_size * self.grid_size, height], chunks=self.chunk_size
)
src = Cube(data)
return src

def add_src_metadata(self, cube):
from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__mesh_to_MeshInfo import (
_gridlike_mesh,
)

mesh = _gridlike_mesh(self.grid_size, self.grid_size)
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
cube.add_aux_coord(mesh_coord_x, 0)
cube.add_aux_coord(mesh_coord_y, 0)
return cube


class PerformScalabilityGridToMesh(PerformScalabilityGridToGrid):
regridder = GridToMeshESMFRegridder

def setup_cache(self):
return super().setup_cache()

def tgt_cube(self):
from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__mesh_to_MeshInfo import (
_gridlike_mesh,
)

tgt = Cube(np.ones([self.target_grid_size * self.target_grid_size]))
mesh = _gridlike_mesh(self.target_grid_size, self.target_grid_size)
mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face")
tgt.add_aux_coord(mesh_coord_x, 0)
tgt.add_aux_coord(mesh_coord_y, 0)
return tgt
19 changes: 15 additions & 4 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,18 @@ def tests(session: nox.sessions.Session):

@nox.session
@nox.parametrize(
["ci_mode", "gh_pages"],
[(True, False), (False, False), (False, True)],
ids=["ci compare", "full", "full then publish"],
["ci_mode", "long_mode", "gh_pages"],
[
(True, False, False),
(False, False, False),
(False, False, True),
(False, True, False),
],
ids=["ci compare", "full", "full then publish", "long snapshot"],
)
def benchmarks(session: nox.sessions.Session, ci_mode: bool, gh_pages: bool):
def benchmarks(
session: nox.sessions.Session, ci_mode: bool, long_mode: bool, gh_pages: bool
):
"""
Perform esmf-regrid performance benchmarks (using Airspeed Velocity).

Expand All @@ -353,6 +360,8 @@ def benchmarks(session: nox.sessions.Session, ci_mode: bool, gh_pages: bool):
ci_mode: bool
Run a cut-down selection of benchmarks, comparing the current commit to
the last commit for performance regressions.
long_mode: bool
Run the long running benchmarks at the current head of the repo.
gh_pages: bool
Run ``asv gh-pages --rewrite`` once finished.

Expand Down Expand Up @@ -383,6 +392,8 @@ def asv_exec(*sub_args: str) -> None:
asv_exec("continuous", previous_commit, "HEAD", "--bench=ci")
finally:
asv_exec("compare", previous_commit, "HEAD")
elif long_mode:
asv_exec("run", "HEAD^!", "--bench=long")
else:
# f32f23a5 = first supporting commit for nox_asv_plugin.py .
asv_exec("run", "f32f23a5..HEAD")
Expand Down