Skip to content

Optimize _get_faces_constLat_intersection_info - #1799

Draft
cmdupuis3 wants to merge 5 commits into
UXARRAY:mainfrom
cmdupuis3:cmd/optimize_integrate
Draft

cmdupuis3 wants to merge 5 commits into
UXARRAY:mainfrom
cmdupuis3:cmd/optimize_integrate

Conversation

@cmdupuis3

@cmdupuis3 cmdupuis3 commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1570

Maybe related to #790

Overview

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 (_)

AI Disclosure

AI Usage: Claude Opus 5

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

@cmdupuis3 cmdupuis3 self-assigned this Sep 30, 2026
@cmdupuis3 cmdupuis3 added scalability Related to scalability & performance efforts run-benchmark Run ASV benchmark workflow labels Sep 30, 2026
cmdupuis3 and others added 2 commits September 30, 2026 13:30
Adapt the Numba intersection kernel to two changes on main:

- UXARRAY#1727 changed gca_const_lat_intersection to return a tuple of tuples
  instead of a (2, 3) array, so the kernel now reads points as
  intersections[r][i] rather than intersections[r, i].
- UXARRAY#1705 made the no-intersection and concave-face errors format the face
  array into their messages, which Numba cannot do, so both raises move
  from the kernel into the Python wrapper. The kernel now also returns the
  number of non-dummy edges that the concave check needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [0287eb0] After [db49700] Ratio Benchmark (Parameter)
- 88.2±5ms 77.5±1ms 0.88 connectivity.Connectivity.time_node_face('120km')
- 2.00±0s 15.3±0.2ms 0.01 mpas_ocean.ZonalAverage.time_zonal_average_robust('120km')
- 500±2ms 5.74±0.04ms 0.01 mpas_ocean.ZonalAverage.time_zonal_average_robust('480km')

Benchmarks that have stayed the same:

Change Before [0287eb0] After [db49700] Ratio Benchmark (Parameter)
1.02±0.01ms 992±10μs 0.97 connectivity.Connectivity.time_edge_face('120km')
454±10μs 455±7μs 1.00 connectivity.Connectivity.time_edge_face('480km')
3.60±0.05ms 3.61±0.07ms 1.00 connectivity.Connectivity.time_edge_node('120km')
1.12±0.04ms 1.12±0.04ms 1.00 connectivity.Connectivity.time_edge_node('480km')
66.6±10μs 71.6±10μs 1.08 connectivity.Connectivity.time_face_edge('120km')
64.8±10μs 68.0±10μs 1.05 connectivity.Connectivity.time_face_edge('480km')
769±10μs 762±20μs 0.99 connectivity.Connectivity.time_face_face('120km')
345±6μs 355±20μs 1.03 connectivity.Connectivity.time_face_face('480km')
56.4±6μs 62.7±6μs ~1.11 connectivity.Connectivity.time_face_node('120km')
55.8±8μs 57.6±6μs 1.03 connectivity.Connectivity.time_face_node('480km')
446±10μs 464±30μs 1.04 connectivity.Connectivity.time_n_nodes_per_face('120km')
366±20μs 410±60μs ~1.12 connectivity.Connectivity.time_n_nodes_per_face('480km')
1.40±0.05ms 1.40±0.03ms 0.99 connectivity.Connectivity.time_node_edge('120km')
459±10μs 472±10μs 1.03 connectivity.Connectivity.time_node_edge('480km')
4.90±0.03ms 4.81±0.04ms 0.98 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')
7.63±0.1ms 7.57±0.07ms 0.99 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
2.40±0.03ms 2.43±0.05ms 1.01 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
9.46±9ms 9.53±1ms 1.01 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.32±0.03ms 1.32±0.01ms 0.99 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.86M 1.86M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
1.85M 1.85M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
2.01M 2.01M 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'))
326M 326M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
326M 327M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
328M 328M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
327M 327M 1.00 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.09±0.02μs 1.10±0.04μs 1.00 geometry_kernels.AccucrossKernels.time_accucross
2.44±0.02μs 2.45±0.04μs 1.00 geometry_kernels.AccucrossKernels.time_accucross_pair
386±5ns 391±10ns 1.01 geometry_kernels.EFTPrimitives.time_acc_sqrt_re
375±20ns 396±30ns 1.06 geometry_kernels.EFTPrimitives.time_diff_of_products
355±10ns 360±20ns 1.02 geometry_kernels.EFTPrimitives.time_two_prod
345±20ns 351±10ns 1.02 geometry_kernels.EFTPrimitives.time_two_sum
591±10ns 596±5ns 1.01 geometry_kernels.GCAConstLatIntersection.time_accux_constlat_kernel
636±20ns 641±4ns 1.01 geometry_kernels.GCAConstLatIntersection.time_gca_const_lat_intersection
706±10ns 711±20ns 1.01 geometry_kernels.GCAConstLatIntersection.time_try_gca_const_lat_intersection
706±10ns 696±10ns 0.99 geometry_kernels.GCAGCAIntersection.time_accux_gca_kernel
856±40ns 826±30ns 0.96 geometry_kernels.GCAGCAIntersection.time_gca_gca_intersection
946±50ns 937±40ns 0.99 geometry_kernels.GCAGCAIntersection.time_try_gca_gca_intersection
36.7±0.6μs 36.8±0.2μs 1.00 geometry_kernels.OrientPredicates.time_on_minor_arc
37.5±1μs 37.1±0.3μs 0.99 geometry_kernels.OrientPredicates.time_orient3d_on_sphere
3.08±0.01ms 3.07±0.01ms 1.00 geometry_samebody.SameBodyConstLat.time_accux_dispatch
1.32±0.01ms 1.32±0ms 1.00 geometry_samebody.SameBodyConstLat.time_accux_kernel
2.09±0.01ms 2.06±0.01ms 0.99 geometry_samebody.SameBodyConstLat.time_fp64_dispatch
151±1μs 151±0.2μs 1.00 geometry_samebody.SameBodyConstLat.time_fp64_kernel
27.7±0.04ms 28.5±0.09ms 1.03 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_dispatch
6.77±0ms 6.86±0.02ms 1.01 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_kernel
21.1±0.1ms 21.1±0.2ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_dispatch
864±2μs 866±2μs 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_kernel
908±8ms 903±8ms 1.00 import.Imports.timeraw_import_uxarray
284M 284M 1.00 import.Imports.track_peakmem_import_uxarray
2.04±0.05ms 1.95±0.02ms 0.96 mpas_ocean.CheckNorm.time_check_norm('120km')
1.54±0.01ms 1.55±0.03ms 1.00 mpas_ocean.CheckNorm.time_check_norm('480km')
1.03±0.01ms 1.02±0.01ms 1.00 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
499±4μs 488±9μs 0.98 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
617±10μs 601±7μs 0.98 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
545±4μs 539±7μs 0.99 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
4.95±0.04ms 4.95±0.07ms 1.00 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
3.36±0.02ms 3.39±0.04ms 1.01 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
98.7±0.2ms 99.0±0.3ms 1.00 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
9.57±0.6ms 9.12±0.04ms 0.95 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')
20.8±0.01ms 20.9±0.07ms 1.00 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
1.07±0.02ms 1.03±0.02ms 0.96 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
10.6±0.05ms 10.5±0.05ms 0.99 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
623±20μs 623±30μs 1.00 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
541±2ms 526±5ms 0.97 mpas_ocean.CrossSections.time_const_lat('120km', 1)
271±2ms 266±2ms 0.98 mpas_ocean.CrossSections.time_const_lat('120km', 2)
139±0.5ms 139±2ms 1.00 mpas_ocean.CrossSections.time_const_lat('120km', 4)
480±10ms 475±7ms 0.99 mpas_ocean.CrossSections.time_const_lat('480km', 1)
241±3ms 241±3ms 1.00 mpas_ocean.CrossSections.time_const_lat('480km', 2)
123±2ms 124±2ms 1.01 mpas_ocean.CrossSections.time_const_lat('480km', 4)
346M 346M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 1)
347M 346M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 2)
347M 346M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 4)
330M 329M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 1)
329M 329M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 2)
330M 329M 1.00 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 4)
23.8±0.3ms 23.2±0.1ms 0.98 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
2.67±0.02ms 2.59±0.02ms 0.97 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
14.2±0.6ms 13.9±0.9ms 0.98 mpas_ocean.FaceAreas.time_face_areas('120km')
3.82±0.2ms 3.71±0.1ms 0.97 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')
733k 733k 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('480km')
308±9ms 303±3ms 0.98 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
53.8±3ms 49.2±1ms 0.91 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
36.4±0.5ms 36.9±0.4ms 1.01 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
5.39±0.2ms 5.18±0.5ms 0.96 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
13.5±0.05ms 13.4±0.3ms 0.99 mpas_ocean.Gradient.time_gradient('120km')
1.72±0.01ms 1.69±0.01ms 0.98 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')
346M 342M 0.99 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('120km')
322M 322M 1.00 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('480km')
256±5μs 266±7μs 1.04 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
125±1μs 124±2μs 0.99 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
182±4μs 180±2μs 0.99 mpas_ocean.Integrate.time_integrate('120km')
165±1μs 162±1μs 0.98 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')
192±4ms 187±0.8ms 0.97 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
193±3ms 186±0.7ms 0.97 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
190±4ms 187±1ms 0.98 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
13.2±0.06ms 13.3±0.1ms 1.00 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
13.2±0.1ms 13.1±0.03ms 1.00 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
13.3±0.09ms 13.1±0.09ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
272±1ms 272±1ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 1.0)
1.43±0.01s 1.43±0s 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 15.0)
563±1ms 561±0.9ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 5.0)
14.6±0.03ms 14.7±0.03ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('480km', 1.0)
28.0±0.1ms 27.8±0.05ms 0.99 mpas_ocean.NeighborhoodBuild.time_build('480km', 15.0)
18.1±0.04ms 18.1±0.09ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('480km', 5.0)
267±1ms 266±0.5ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 1.0)
1.42±0s 1.41±0s 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 15.0)
556±3ms 555±2ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 5.0)
14.3±0.06ms 14.2±0.04ms 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 1.0)
27.3±0.04ms 27.5±0.1ms 1.01 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 15.0)
17.6±0.04ms 17.6±0.06ms 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)
41.6±0.4ms 41.5±0.6ms 1.00 mpas_ocean.NeighborhoodDask.time_mean('120km', 'grid_chunks')
18.8±0.2ms 18.7±0.05ms 0.99 mpas_ocean.NeighborhoodDask.time_mean('120km', 'numpy')
38.0±0.5ms 37.8±0.7ms 0.99 mpas_ocean.NeighborhoodDask.time_mean('120km', 'time_chunks')
10.1±0.05ms 9.95±0.1ms 0.99 mpas_ocean.NeighborhoodDask.time_mean('480km', 'grid_chunks')
730±9μs 715±10μs 0.98 mpas_ocean.NeighborhoodDask.time_mean('480km', 'numpy')
6.89±0.1ms 6.66±0.1ms 0.97 mpas_ocean.NeighborhoodDask.time_mean('480km', 'time_chunks')
5.82M 5.83M 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')
678k 678k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'grid_chunks')
177k 177k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'numpy')
539k 532k 0.99 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'time_chunks')
13.8±0s 13.8±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'mean')
14.6±0.01s 14.6±0.03s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'median')
249±1ms 247±0.7ms 0.99 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'mean')
253±0.5ms 253±0.5ms 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'median')
1.47±0s 1.47±0s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'mean')
1.69±0.01s 1.68±0s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'median')
28.5±0.1ms 28.4±0.06ms 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'mean')
29.9±0.1ms 29.6±0.05ms 0.99 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'median')
35.1±0.2ms 34.8±0.2ms 0.99 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'mean')
259±0.5ms 259±0.4ms 1.00 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'median')
535±20μs 510±10μs 0.95 mpas_ocean.NeighborhoodReduce.time_reduce('480km', 'mean')
1.95±0.05ms 1.90±0.02ms 0.97 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.5k 19.5k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'mean')
20k 20k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'median')
374±20μs 360±40μs 0.96 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
363±10μs 341±20μs 0.94 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
357±10μs 332±7μs 0.93 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')
328±30μs 324±10μs 0.99 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
105±1ms 104±0.3ms 0.99 mpas_ocean.RemapDownsample.time_bilinear_remapping
17.2±0.2ms 17.1±0.3ms 0.99 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
15.6±0.3ms 15.3±0.07ms 0.98 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
983±9ms 964±20ms 0.98 mpas_ocean.RemapUpsample.time_bilinear_remapping
26.1±0.04ms 26.1±0.02ms 1.00 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
10.7±0.05ms 10.6±0.02ms 0.99 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
8.27±0.1ms 8.06±0.2ms 0.98 mpas_ocean.ZonalAverage.time_zonal_average('120km')
4.86±0.1ms 4.72±0.06ms 0.97 mpas_ocean.ZonalAverage.time_zonal_average('480km')
347M 347M 1.00 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('120km')
330M 330M 1.00 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('480km')
1.0299644105176622 1.0317901352837642 1.00 nogil_scaling.GILScaling.track_gil_scaling
7.03±0.1ms 6.84±0.02ms 0.97 quad_hexagon.QuadHexagon.time_open_dataset
5.79±0.03ms 5.74±0.02ms 0.99 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
73.2k 72.5k 0.99 quad_hexagon.QuadHexagon.track_peakmem_open_dataset
71.8k 71.6k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_grid
1.17±0s 1.16±0s 0.99 to_raster.ToRaster.time_to_raster((10000.0, 10.0))
2.86±0.03s 2.82±0.03s 0.99 to_raster.ToRaster.time_to_raster((10000.0, 100.0))
3.28±0.01s 3.32±0.02s 1.01 to_raster.ToRaster.time_to_raster((100000.0, 100.0))
271M 271M 1.00 to_raster.ToRaster.track_peakmem((10000.0, 10.0))
684M 684M 1.00 to_raster.ToRaster.track_peakmem((10000.0, 100.0))
768M 768M 1.00 to_raster.ToRaster.track_peakmem((100000.0, 100.0))

cmdupuis3 and others added 2 commits September 30, 2026 14:08
… Numba

The robust path built several tiny polars DataFrames per candidate face,
and that fixed per-call overhead was nearly its whole runtime. Each
latitude is now two Numba calls: one computing every candidate face's
longitude intervals, and one running the overlap sweep line. Results are
bit-identical, and robust zonal means are 52-86x faster, now within
1.3-2.7x of the default path instead of 100-200x slower.

Both new per-latitude kernels release the GIL. Each call does milliseconds
of work, so from 4 threads the robust weights take 1.09x the single-thread
wall time instead of 4.03x.

The private helpers now use arrays instead of DataFrames:
_zonal_face_weights_robust returns the weights, _get_zonal_face_interval
returns (start, end) rows, and _process_overlapped_intervals takes
starts/ends/face indices and returns per-face contributions. A face whose
crossing longitudes cannot be paired into intervals now raises ValueError
rather than polars' ShapeError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unoptimized routines in integrate.py

1 participant