Skip to content

Speeds up to_raster() by improving efficiency of Grid.get_faces_containing_point() - #1809

Open
Sevans711 wants to merge 11 commits into
mainfrom
sevans/to_raster-speedup
Open

Sevans711 wants to merge 11 commits into
mainfrom
sevans/to_raster-speedup

Conversation

@Sevans711

@Sevans711 Sevans711 commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1796 (sub-issue of #1648)
Closes #1358

Overview

Speeds up to_raster() by refactoring to avoid creating tiny numpy arrays inside numba routines (see #1648 and other sub-issues of it for more details). As mentioned in #1796 (comment), to_raster() was incredibly slow when applied to grids with varying resolution, causing many candidate faces per point, and calling relevant numba methods from point_in_face.py many times.

This work also happens to close #1358; the NumbaPerformanceWarning mentioned there is no longer being raised after these refactors.

The refactors here aren't just purely swapping numpy arrays for tuples, and numpy operations for uxarray.utils.numba_math.py operations. The biggest reason for this is that simply replacing those things in-place wouldn't be sufficient get around the issue with _get_cartesian_face_edge_nodes(), because n_edges can vary from face to face. This PR refactors to entirely avoid using that function anywhere in to_raster's call stack; the old _face_contains_point(face_edges, point) has been replaced by a new _point_in_face(point, nodes_idx, node_x, node_y, node_z). Other parts of the code which used _face_contains_point have been updated accordingly, and the new _point_in_face_from_grid provides a convenient way to call this (especially for tests).

Overall, the changes follow the descriptions laid out by #1796. Nothing should be too surprising in here. Added benchmarks to test to_raster() across a variety of grid sizes and resolution-variabilities (ratio between largest-area face and smallest-area face).

Performance notes from ASV benchmarks:

  • (EDIT: note that these conclusions were from before commenting out many of the parameter combinations, as per comment below.) Speedups increase with increased resolution variability (ratio between largest area and smallest area of any face), but remain roughly constant as a function of total number of faces.
    • Roughly 7x speedup for "resolution_variability=10" cases, across all tested n_face: 1e3, 1e4, 1e5.
    • Roughly 10x speedup for "resolution_variability=100" cases, across all tested n_face: n_face=1e3, 1e4, 1e5.
    • At least 13x speedup for "resolution_variability=1000" cases, across all tested n_face: n_face=1e4, 1e5. Exact speedup unknown, suite shows "failed" on main because they took longer than 180 seconds, which is what I chose for the timeout for these cases.
      • (Locally, saw roughly 13.5x speedup during a spot check with n_face=1e4, resolution_variability=1000.)
  • Many peakmem checks fail on main, due to timeout (it takes longer because of the with numba_threads(1): block).

Sidenote: the benchmarks suite takes roughly 70 mins to run on this branch due to the cases which currently timeout on main. However, the longest benchmark within this commit itself is still less than 15 seconds, so once this becomes the new baseline, I wouldn't expect a huge increase in benchmark suite runtime in the future. EDIT: no longer relevant, after commenting out many of the parameters; took roughly 30 mins to run the suite here, which is comparable to how long it took in #1710.

PR Checklist

General

  • An issue is created and linked
  • Added appropriate labels (if your uxarray repo permissions allow it)
  • Filled out Overview and Expected Usage (if applicable) sections

Testing & Benchmarking

  • There is adequate test coverage of changes from this PR (add new tests if needed)
  • If this PR could affect performance, ran ASV benchmarks and confirmed they show expected behavior (add a new benchmark if necessary)

Documentation and Examples

  • Docstrings updated with any function changes, and included in all new functions
  • User (public) functions added to docs/api.rst; internal (private) function names start with an underscore (_)
  • [N/A] If touched any notebook files, cleared the output of all cells before committing
  • [N/A] If added new notebook files, put into appropriate directories and referenced in appropriate files

AI Disclosure

AI Usage: Claude conversations (especially for building benchmarks) and GitHub Copilot's inline code suggestions

  • I have tested and take responsibility for all AI-generated content in my PR.

Checked an example raster is equivalent (via casting to xr.DataArray then checking .equals) before and after this commit.
renames old _face_contains_point to _face_contains_point_from_edges, for now. Next, delete it entirely, and rewrite call sites appropriately to use the new _point_in_face syntax instead.

Confirmed that a raster is exactly the same, before and after these changes. Only a small speedup so far (~4.70s to ~4.45s), but there are still more optimizations to do!
Something in test_cross_sections.py is breaking now (numba python types compiler errors) but all other tests pass.
(there are no more references to it; forgot to remove it during previous commit.)
and _point_in_face, by avoiding tiny numpy arrays.

Still only a small speedup for to_raster though... ~4.30s compared to ~4.45s from 594739b (and ~4.70s on main). Confirmed that the pytest suite passes locally, and that a produced raster is exactly the same as on main, via .equals().
Although, this actually provides no noticeable speed improvement to to_raster()...
@Sevans711 Sevans711 added scalability Related to scalability & performance efforts run-benchmark Run ASV benchmark workflow visualization Plotting or other visualizations labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [704d92b] After [8a8a226] Ratio Benchmark (Parameter)
- 415±10μs 357±40μs 0.86 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
- 388±10μs 328±30μs 0.85 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
- 131±0.6ms 110±2ms 0.84 mpas_ocean.RemapDownsample.time_bilinear_remapping
- 4.09±0.03s 591±3ms 0.14 to_raster.ToRaster.time_to_raster((1000.0, 10.0))
- 23.0±0.06s 2.40±0.01s 0.10 to_raster.ToRaster.time_to_raster((1000.0, 100.0))
- 9.27±0.01s 1.11±0.01s 0.12 to_raster.ToRaster.time_to_raster((10000.0, 10.0))
* failed 545M n/a to_raster.ToRaster.track_peakmem((1000.0, 100.0))
* failed 271M n/a to_raster.ToRaster.track_peakmem((10000.0, 10.0))

Benchmarks that have stayed the same:

Change Before [704d92b] After [8a8a226] Ratio Benchmark (Parameter)
1.01±0.01ms 972±20μs 0.96 connectivity.Connectivity.time_edge_face('120km')
476±10μs 478±10μs 1.00 connectivity.Connectivity.time_edge_face('480km')
4.11±0.06ms 4.00±0.07ms 0.97 connectivity.Connectivity.time_edge_node('120km')
1.31±0.03ms 1.32±0.01ms 1.01 connectivity.Connectivity.time_edge_node('480km')
74.8±20μs 76.2±4μs 1.02 connectivity.Connectivity.time_face_edge('120km')
72.9±9μs 76.8±8μs 1.05 connectivity.Connectivity.time_face_edge('480km')
1.05±0.02ms 1.02±0.01ms 0.98 connectivity.Connectivity.time_face_face('120km')
448±20μs 426±20μs 0.95 connectivity.Connectivity.time_face_face('480km')
72.0±7μs 67.9±4μs 0.94 connectivity.Connectivity.time_face_node('120km')
65.8±4μs 67.5±20μs 1.03 connectivity.Connectivity.time_face_node('480km')
500±20μs 504±40μs 1.01 connectivity.Connectivity.time_n_nodes_per_face('120km')
437±20μs 428±50μs 0.98 connectivity.Connectivity.time_n_nodes_per_face('480km')
1.42±0.03ms 1.39±0.07ms 0.98 connectivity.Connectivity.time_node_edge('120km')
487±10μs 506±20μs 1.04 connectivity.Connectivity.time_node_edge('480km')
84.8±3ms 79.9±2ms 0.94 connectivity.Connectivity.time_node_face('120km')
5.10±0.04ms 5.12±0.05ms 1.00 connectivity.Connectivity.time_node_face('480km')
1.42M 1.42M 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_edge_face('120km')
106k 106k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_edge_face('480km')
6.48M 6.48M 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_edge_node('120km')
420k 420k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_edge_node('480km')
2.42k 2.42k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_edge('120km')
2.42k 2.42k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_edge('480km')
1.6M 1.6M 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_face('120km')
101k 101k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_face('480km')
2.54k 2.54k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_node('120km')
2.54k 2.54k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_face_node('480km')
240k 240k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_n_nodes_per_face('120km')
26.3k 26.3k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_n_nodes_per_face('480km')
1.9M 1.9M 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_node_edge('120km')
127k 127k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_node_edge('480km')
11.9M 11.9M 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_node_face('120km')
747k 747k 1.00 connectivity.ConnectivityTracemalloc.track_peakmem_node_face('480km')
8.68±0.1ms 8.40±0.08ms 0.97 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
2.75±0.03ms 2.68±0.05ms 0.98 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
10.2±9ms 10.2±0.1ms 1.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.62±0.02ms 1.55±0.05ms 0.96 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
57.3k 57.3k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
12.3k 12.3k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
123k 123k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
128 128 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.27M 1.27M 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
50.1k 50.1k 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
1.48M 1.48M 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
712 712 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.98M 1.98M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
1.97M 1.97M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
2.14M 2.14M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
35.6k 35.6k 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
318M 318M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
318M 318M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
319M 319M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
318M 319M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.21±0.02μs 1.16±0.02μs 0.96 geometry_kernels.AccucrossKernels.time_accucross
2.60±0.01μs 2.58±0.03μs 0.99 geometry_kernels.AccucrossKernels.time_accucross_pair
451±20ns 440±8ns 0.98 geometry_kernels.EFTPrimitives.time_acc_sqrt_re
441±8ns 436±9ns 0.99 geometry_kernels.EFTPrimitives.time_diff_of_products
380±10ns 400±20ns 1.05 geometry_kernels.EFTPrimitives.time_two_prod
390±4ns 386±10ns 0.99 geometry_kernels.EFTPrimitives.time_two_sum
1.04±0.4μs 667±30ns ~0.64 geometry_kernels.GCAConstLatIntersection.time_accux_constlat_kernel
716±20ns 721±20ns 1.01 geometry_kernels.GCAConstLatIntersection.time_gca_const_lat_intersection
827±10ns 826±20ns 1.00 geometry_kernels.GCAConstLatIntersection.time_try_gca_const_lat_intersection
782±20ns 802±40ns 1.03 geometry_kernels.GCAGCAIntersection.time_accux_gca_kernel
892±10ns 911±50ns 1.02 geometry_kernels.GCAGCAIntersection.time_gca_gca_intersection
1.06±0.03μs 1.05±0.02μs 0.99 geometry_kernels.GCAGCAIntersection.time_try_gca_gca_intersection
52.5±5μs 50.3±0.9μs 0.96 geometry_kernels.OrientPredicates.time_on_minor_arc
52.9±0.8μs 50.1±4μs 0.95 geometry_kernels.OrientPredicates.time_orient3d_on_sphere
3.05±0.01ms 3.07±0.02ms 1.00 geometry_samebody.SameBodyConstLat.time_accux_dispatch
1.16±0.01ms 1.16±0ms 1.00 geometry_samebody.SameBodyConstLat.time_accux_kernel
2.29±0.01ms 2.29±0.01ms 1.00 geometry_samebody.SameBodyConstLat.time_fp64_dispatch
148±0.7μs 148±1μs 1.00 geometry_samebody.SameBodyConstLat.time_fp64_kernel
29.6±0.1ms 29.4±0.01ms 0.99 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_dispatch
6.32±0.02ms 6.30±0.03ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_kernel
23.7±0.06ms 23.6±0.02ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_dispatch
859±1μs 860±0.8μs 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_kernel
921±10ms 934±9ms 1.01 import.Imports.timeraw_import_uxarray
275M 275M 1.00 import.Imports.track_peakmem_import_uxarray
2.55±0.08ms 2.55±0.03ms 1.00 mpas_ocean.CheckNorm.time_check_norm('120km')
2.09±0.04ms 2.06±0.03ms 0.98 mpas_ocean.CheckNorm.time_check_norm('480km')
1.16±0.01ms 1.15±0.03ms 0.99 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
554±8μs 564±10μs 1.02 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
664±10μs 664±20μs 1.00 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
614±10μs 604±10μs 0.98 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
5.37±0.03ms 5.64±0.2ms 1.05 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
3.88±0.02ms 3.81±0.02ms 0.98 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
96.6±0.2ms 96.4±0.2ms 1.00 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
9.96±0.3ms 9.39±0.1ms 0.94 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')
18.7±0.03ms 18.9±0.04ms 1.01 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
1.00±0.03ms 977±20μs 0.97 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
10.0±0.03ms 9.95±0.04ms 0.99 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
615±20μs 626±20μs 1.02 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
612±6ms 613±3ms 1.00 mpas_ocean.CrossSections.time_const_lat('120km', 1)
306±5ms 300±1ms 0.98 mpas_ocean.CrossSections.time_const_lat('120km', 2)
159±2ms 158±5ms 0.99 mpas_ocean.CrossSections.time_const_lat('120km', 4)
566±5ms 546±6ms 0.96 mpas_ocean.CrossSections.time_const_lat('480km', 1)
288±0.3ms 274±3ms 0.95 mpas_ocean.CrossSections.time_const_lat('480km', 2)
142±0.9ms 140±0.8ms 0.99 mpas_ocean.CrossSections.time_const_lat('480km', 4)
338M 338M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 1)
338M 337M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 2)
338M 338M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 4)
321M 321M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 1)
321M 321M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 2)
321M 321M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 4)
26.4±0.2ms 25.9±0.2ms 0.98 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
3.09±0.03ms 2.91±0.02ms 0.94 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
14.6±0.5ms 14.9±0.6ms 1.02 mpas_ocean.FaceAreas.time_face_areas('120km')
4.43±0.3ms 4.43±0.2ms 1.00 mpas_ocean.FaceAreas.time_face_areas('480km')
229k 229k 1.00 mpas_ocean.FaceAreas.track_nbytes_face_areas('120km')
14.3k 14.3k 1.00 mpas_ocean.FaceAreas.track_nbytes_face_areas('480km')
2.12M 2.12M 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('120km')
720k 720k 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('480km')
915±10ms 901±10ms 0.99 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
51.4±0.4ms 52.8±2ms 1.03 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
84.4±2ms 83.2±0.4ms 0.98 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
5.28±0.3ms 5.53±0.3ms 1.05 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
13.4±0.1ms 13.5±0.4ms 1.00 mpas_ocean.Gradient.time_gradient('120km')
1.80±0.01ms 1.83±0.02ms 1.02 mpas_ocean.Gradient.time_gradient('480km')
457k 457k 1.00 mpas_ocean.Gradient.track_nbytes_gradient('120km')
28.7k 28.7k 1.00 mpas_ocean.Gradient.track_nbytes_gradient('480km')
3.2M 3.2M 1.00 mpas_ocean.Gradient.track_peakmem_gradient('120km')
204k 204k 1.00 mpas_ocean.Gradient.track_peakmem_gradient('480km')
333M 333M 1.00 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('120km')
317M 314M 0.99 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('480km')
280±10μs 279±10μs 1.00 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
162±10μs 148±4μs 0.92 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
207±9μs 207±8μs 1.00 mpas_ocean.Integrate.time_integrate('120km')
193±30μs 193±6μs 1.00 mpas_ocean.Integrate.time_integrate('480km')
18.4M 18.4M 1.00 mpas_ocean.Integrate.track_nbytes_integrate('120km')
1.2M 1.2M 1.00 mpas_ocean.Integrate.track_nbytes_integrate('480km')
185±2ms 182±1ms 0.98 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
186±4ms 182±2ms 0.98 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
184±2ms 182±0.7ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
13.3±0.05ms 13.1±0.06ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
13.1±0.1ms 13.1±0.05ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
13.1±0.2ms 13.1±0.3ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
247±2ms 245±1ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('120km', 1.0)
1.31±0.01s 1.30±0s 0.99 mpas_ocean.NeighborhoodBuild.time_build('120km', 15.0)
508±3ms 504±2ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('120km', 5.0)
13.5±0.1ms 13.4±0.1ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('480km', 1.0)
25.4±0.2ms 25.1±0.03ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('480km', 15.0)
16.5±0.03ms 16.4±0.05ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('480km', 5.0)
241±0.09ms 242±1ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 1.0)
1.29±0s 1.28±0s 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 15.0)
505±3ms 500±1ms 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 5.0)
12.9±0.03ms 12.9±0.06ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 1.0)
24.9±0.03ms 24.7±0.07ms 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 15.0)
16.1±0.06ms 16.1±0.05ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 5.0)
1.19 1.19 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('120km', 1.0)
612.76 612.76 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('120km', 15.0)
74.17 74.17 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('120km', 5.0)
1.0 1.0 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('480km', 1.0)
37.29 37.29 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('480km', 15.0)
6.57 6.57 1.00 mpas_ocean.NeighborhoodBuild.track_mean_neighbors('480km', 5.0)
728k 728k 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('120km', 1.0)
141M 141M 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('120km', 15.0)
17.4M 17.4M 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('120km', 5.0)
43k 43k 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('480km', 1.0)
563k 563k 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('480km', 15.0)
123k 123k 1.00 mpas_ocean.NeighborhoodBuild.track_nbytes_neighbors('480km', 5.0)
5.72M 5.72M 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('120km', 1.0)
145M 145M 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('120km', 15.0)
21.5M 21.5M 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('120km', 5.0)
362k 362k 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('480km', 1.0)
825k 825k 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('480km', 15.0)
384k 384k 1.00 mpas_ocean.NeighborhoodBuild.track_peakmem_build('480km', 5.0)
40.1±0.4ms 40.0±0.5ms 1.00 mpas_ocean.NeighborhoodDask.time_mean('120km', 'grid_chunks')
18.5±0.08ms 18.3±0.2ms 0.99 mpas_ocean.NeighborhoodDask.time_mean('120km', 'numpy')
36.2±0.5ms 36.0±0.6ms 0.99 mpas_ocean.NeighborhoodDask.time_mean('120km', 'time_chunks')
11.2±0.2ms 10.9±0.06ms 0.98 mpas_ocean.NeighborhoodDask.time_mean('480km', 'grid_chunks')
804±9μs 813±20μs 1.01 mpas_ocean.NeighborhoodDask.time_mean('480km', 'numpy')
7.85±0.2ms 7.51±0.1ms 0.96 mpas_ocean.NeighborhoodDask.time_mean('480km', 'time_chunks')
5.81M 5.81M 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('120km', 'grid_chunks')
2.75M 2.75M 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('120km', 'numpy')
5.68M 5.68M 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('120km', 'time_chunks')
691k 691k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'grid_chunks')
177k 177k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'numpy')
540k 538k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'time_chunks')
12.6±0.02s 12.6±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'mean')
13.2±0.03s 13.2±0s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'median')
226±0.6ms 227±2ms 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'mean')
232±0.4ms 230±0.7ms 0.99 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'median')
1.34±0.01s 1.34±0s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'mean')
1.53±0s 1.54±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'median')
25.8±0.2ms 25.9±0.3ms 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'mean')
27.0±0.1ms 27.0±0.1ms 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'median')
31.1±0.2ms 31.1±0.1ms 1.00 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'mean')
229±1ms 229±0.9ms 1.00 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'median')
469±30μs 485±40μs 1.03 mpas_ocean.NeighborhoodReduce.time_reduce('480km', 'mean')
1.77±0.05ms 1.73±0.03ms 0.98 mpas_ocean.NeighborhoodReduce.time_reduce('480km', 'median')
239k 239k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('120km', 'mean')
245k 245k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('120km', 'median')
19.4k 19.4k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'mean')
19.9k 19.9k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'median')
421±20μs 373±30μs ~0.89 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
399±20μs 346±40μs ~0.87 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')
17.4±0.3ms 17.4±0.1ms 1.00 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
15.6±0.2ms 15.7±0.07ms 1.01 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
1.44±0.01s 1.10±0.02s ~0.76 mpas_ocean.RemapUpsample.time_bilinear_remapping
26.5±0.3ms 26.6±0.4ms 1.00 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
11.6±0.1ms 11.7±0.1ms 1.00 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
8.41±0.3ms 8.58±0.1ms 1.02 mpas_ocean.ZonalAverage.time_zonal_average('120km')
5.28±0.04ms 5.25±0.06ms 0.99 mpas_ocean.ZonalAverage.time_zonal_average('480km')
339M 339M 1.00 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('120km')
322M 322M 1.00 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('480km')
1.032798442722082 1.0222875296685547 0.99 nogil_scaling.GILScaling.track_gil_scaling
7.81±0.05ms 7.77±0.2ms 0.99 quad_hexagon.QuadHexagon.time_open_dataset
6.63±0.05ms 6.42±0.03ms 0.97 quad_hexagon.QuadHexagon.time_open_grid
408 408 1.00 quad_hexagon.QuadHexagon.track_nbytes_open_dataset
392 392 1.00 quad_hexagon.QuadHexagon.track_nbytes_open_grid
72.6k 72.6k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_dataset
71.8k 71.8k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_grid
121M 121M 1.00 to_raster.ToRaster.track_peakmem((1000.0, 10.0))

@Sevans711
Sevans711 marked this pull request as ready for review October 6, 2026 15:12
@cmdupuis3

Copy link
Copy Markdown
Collaborator

For the benchmarks, can you trim them down a bit? I think we probably only need two scales for the timing and peakmem benchmarks, atm there are 7-8. As long as we can see the scaling difference we expect, we don't really need the rest.

@Sevans711

Copy link
Copy Markdown
Collaborator Author

For the benchmarks, can you trim them down a bit? I think we probably only need two scales for the timing and peakmem benchmarks, atm there are 7-8. As long as we can see the scaling difference we expect, we don't really need the rest.

Definitely, that makes sense. I trimmed it to just 3 parameter pairings: (n_face, area_ratio) = (1e4, 10), (1e4, 100), (1e5, 100).

Comparing (1e4, 10) to (1e4, 100) shows scaling with resolution variability, so we can learn lessons like "10x more resolution variability increases costs by roughly 3x."

Comparing (1e4, 100) to (1e5, 100) shows scaling with number of faces, so we can learn lessons like "10x more faces increases costs by only roughly 10%.

The other parameter pairings help build a clearer picture across a wider parameter space, but it does make sense to me that we should pick a minimal set for the benchmark suite. I didn't trim down further to 2 pairings because that would make it impossible to separate the "resolution variability" scaling from the "number of faces" scaling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-benchmark Run ASV benchmark workflow scalability Related to scalability & performance efforts visualization Plotting or other visualizations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed up to_raster() by improving efficiency of Grid.get_faces_containing_point Use contiguous arrays for point containment checks

2 participants