Skip to content

Cached benchmark file I/O - #1700

Draft
cmdupuis3 wants to merge 21 commits into
UXARRAY:mainfrom
cmdupuis3:cmd/bench_io
Draft

Cached benchmark file I/O#1700
cmdupuis3 wants to merge 21 commits into
UXARRAY:mainfrom
cmdupuis3:cmd/bench_io

Conversation

@cmdupuis3

@cmdupuis3 cmdupuis3 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Closes #1698

Overview

This PR is a large-scale overhaul of the benchmarks to support single-loads of files per benchmark suite.

Currently, the ASV benchmark suite runs a new process per benchmark, requiring full-scale loads of files for every single benchmark. There are a limited number of files that are actually used to benchmark, so we end up loading many files dozens of times, running up wallclock time on the benchmark suite as a whole. This PR instead has a unified approach, where a parent process is launched for the whole benchmark suite, uncached files are retrieved into /uxarray/benchmarks/_io_cache/, and benchmarks are spawned on forked processes that inherit visibility into the file cache and some other properties.

Simultaneously, this reorganization of file I/O will help to expand the scope of what most non-I/O benchmarks are able to test, since some of them were artificially restricted to smaller grids. That raises another question about how many benchmarks really need to be run at every resolution, but I think that's out of scope for this PR, since it would alter the actual benchmark suite, rather than just changing how they are run.

General CI runs should see a wallclock improvement for whole benchmark suites. CI benchmark runs are showing improvements of about 19 minutes so far (38min -> 19min). Runs on HPC can expect larger improvements. Larger benchmark suites will benefit more from cached file I/O, while benchmarks run individually won't see much improvement.

Other general speed improvements:

  1. Test processes forking from warm-cache parent process
  2. Async dispatch of tests on file arrival, so large files don't stall tests of smaller files.
  3. Sample generation on parent process for reuse in forked processes; sacrifices a bit of robustness for significant speed gains in geometry routines.

This PR has merged in PR #1708 already, since it is a precondition for the optimizations here to be thread-safe.

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

  • Adequate tests are created if there is new functionality
  • Tests are not too basic (such as simply calling a function and nothing else)
  • Tests cover all major paths in your new functions
  • If this PR could affect performance, ran ASV benchmarks and confirmed they show expected behavior (add a new benchmark if necessary)

Documentation

  • Docstrings have been added to all new functions
  • Docstrings have been updated with any function changes
  • Internal (private) function names start with an underscore (_)

AI Disclosure

AI Usage: Claude Opus 5

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

@cmdupuis3 cmdupuis3 added scalability Related to scalability & performance efforts benchmarking Related to benchmarks, memory usage, and/or time profiling labels Aug 21, 2026
@cmdupuis3 cmdupuis3 self-assigned this Aug 21, 2026
@cmdupuis3 cmdupuis3 added the run-benchmark Run ASV benchmark workflow label Aug 21, 2026
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [7eff90d] After [0dded6c] Ratio Benchmark (Parameter)
- 425M 337M 0.79 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
- 443M 369M 0.83 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
- 425M 343M 0.81 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
- 425M 339M 0.8 face_bounds.FaceBoundsColdStartRss.track_peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
- 2.88±0.02ms 2.59±0.01ms 0.9 geometry_samebody.SameBodyConstLat.time_accux_dispatch
- 11.3±0.04s 813±7ms 0.07 import.Imports.timeraw_import_uxarray
- 425M 293M 0.69 import.Imports.track_peakmem_import_uxarray
- 433M 357M 0.82 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 1)
- 431M 358M 0.83 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 2)
- 432M 357M 0.83 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('120km', 4)
- 425M 340M 0.8 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 1)
- 425M 340M 0.8 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 2)
- 425M 340M 0.8 mpas_ocean.CrossSectionsPeakMem.track_peakmem_const_lat('480km', 4)
- 426M 353M 0.83 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('120km')
- 425M 332M 0.78 mpas_ocean.GradientColdStartRss.track_peakmem_gradient('480km')
- 13.8±0.4ms 12.1±0.3ms 0.87 mpas_ocean.NeighborhoodDask.time_mean('480km', 'grid_chunks')
- 10.4±0.2ms 8.84±0.3ms 0.85 mpas_ocean.NeighborhoodDask.time_mean('480km', 'time_chunks')
- 432M 359M 0.83 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('120km')
- 425M 343M 0.81 mpas_ocean.ZonalAveragePeakMem.track_peakmem_zonal_average('480km')

Benchmarks that have stayed the same:

Change Before [7eff90d] After [0dded6c] Ratio Benchmark (Parameter)
193±1ms 193±2ms 1.00 bench_connectivity.Connectivity.time_edge_face('120km')
11.6±0.02ms 11.5±0.03ms 0.99 bench_connectivity.Connectivity.time_edge_face('480km')
192±0.6ms 193±1ms 1.00 bench_connectivity.Connectivity.time_edge_node('120km')
10.6±0.04ms 10.7±0.06ms 1.01 bench_connectivity.Connectivity.time_edge_node('480km')
192±0.3ms 193±1ms 1.01 bench_connectivity.Connectivity.time_face_edge('120km')
10.9±0.08ms 10.9±0.03ms 1.00 bench_connectivity.Connectivity.time_face_edge('480km')
878±5ms 859±10ms 0.98 bench_connectivity.Connectivity.time_face_face('120km')
55.0±0.3ms 55.5±0.2ms 1.01 bench_connectivity.Connectivity.time_face_face('480km')
54.2±0.6μs 53.5±0.5μs 0.99 bench_connectivity.Connectivity.time_face_node('120km')
54.5±0.5μs 52.6±0.2μs 0.97 bench_connectivity.Connectivity.time_face_node('480km')
391±10μs 374±9μs 0.96 bench_connectivity.Connectivity.time_n_nodes_per_face('120km')
312±9μs 318±10μs 1.02 bench_connectivity.Connectivity.time_n_nodes_per_face('480km')
193±0.8ms 193±0.6ms 1.00 bench_connectivity.Connectivity.time_node_edge('120km')
11.0±0.07ms 11.0±0.03ms 1.00 bench_connectivity.Connectivity.time_node_edge('480km')
75.0±1ms 73.5±0.09ms 0.98 bench_connectivity.Connectivity.time_node_face('120km')
5.14±0.1ms 5.04±0.07ms 0.98 bench_connectivity.Connectivity.time_node_face('480km')
8.58±0.08ms 8.54±0.2ms 1.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
2.67±0.05ms 2.65±0.06ms 0.99 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
10.1±10s 9.89±10ms ~0.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.60±0.04ms 1.56±0.03ms 0.97 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.96M 1.96M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
1.95M 1.96M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
2.11M 2.12M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
35.5k 35.4k 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.23±0.05μs 1.18±0.01μs 0.95 geometry_kernels.AccucrossKernels.time_accucross
2.74±0.03μs 2.71±0.04μs 0.99 geometry_kernels.AccucrossKernels.time_accucross_pair
441±10ns 456±30ns 1.03 geometry_kernels.EFTPrimitives.time_acc_sqrt_re
431±30ns 431±20ns 1.00 geometry_kernels.EFTPrimitives.time_diff_of_products
386±20ns 371±4ns 0.96 geometry_kernels.EFTPrimitives.time_two_prod
380±10ns 381±10ns 1.00 geometry_kernels.EFTPrimitives.time_two_sum
1.54±0.03μs 1.58±0.04μs 1.03 geometry_kernels.GCAConstLatIntersection.time_accux_constlat_kernel
1.15±0.02μs 1.15±0.05μs 1.00 geometry_kernels.GCAConstLatIntersection.time_gca_const_lat_intersection
2.01±0.07μs 2.01±0.09μs 1.00 geometry_kernels.GCAConstLatIntersection.time_try_gca_const_lat_intersection
1.70±0.03μs 1.75±0.03μs 1.03 geometry_kernels.GCAGCAIntersection.time_accux_gca_kernel
1.41±0.01μs 1.41±0.03μs 1.00 geometry_kernels.GCAGCAIntersection.time_gca_gca_intersection
2.23±0.04μs 2.26±0.07μs 1.01 geometry_kernels.GCAGCAIntersection.time_try_gca_gca_intersection
52.6±0.8μs 51.0±0.8μs 0.97 geometry_kernels.OrientPredicates.time_on_minor_arc
52.1±0.4μs 50.9±1μs 0.98 geometry_kernels.OrientPredicates.time_orient3d_on_sphere
1.16±0ms 1.16±0ms 1.00 geometry_samebody.SameBodyConstLat.time_accux_kernel
1.81±0.1ms 1.72±0.01ms 0.95 geometry_samebody.SameBodyConstLat.time_fp64_dispatch
144±0.4μs 143±0.3μs 1.00 geometry_samebody.SameBodyConstLat.time_fp64_kernel
33.2±0.3ms 32.4±0.4ms 0.98 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_dispatch
10.2±0.02ms 10.1±0.02ms 0.99 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_kernel
27.0±0.1ms 26.4±0.03ms 0.98 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_dispatch
4.95±0.07ms 4.74±0.01ms 0.96 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_kernel
2.47±0.02ms 2.48±0.02ms 1.01 mpas_ocean.CheckNorm.time_check_norm('120km')
2.07±0.02ms 2.02±0.03ms 0.98 mpas_ocean.CheckNorm.time_check_norm('480km')
833±20ms 811±5ms 0.97 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
52.8±0.4ms 51.8±0.4ms 0.98 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
642±20μs 612±10μs 0.95 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
566±8μs 568±9μs 1.00 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
5.34±0.01ms 5.29±0.01ms 0.99 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
3.85±0.01ms 3.80±0.01ms 0.99 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
95.1±1ms 94.3±0.3ms 0.99 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
10.1±0.2ms 9.17±0.3ms ~0.91 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')
18.6±0.1ms 18.3±0.03ms 0.98 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
965±10μs 949±8μs 0.98 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
10.9±0.09ms 10.9±0.1ms 1.00 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
649±10μs 660±30μs 1.02 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
579±4ms 590±10ms 1.02 mpas_ocean.CrossSections.time_const_lat('120km', 1)
295±0.6ms 290±2ms 0.98 mpas_ocean.CrossSections.time_const_lat('120km', 2)
151±1ms 148±1ms 0.98 mpas_ocean.CrossSections.time_const_lat('120km', 4)
545±6ms 532±5ms 0.98 mpas_ocean.CrossSections.time_const_lat('480km', 1)
270±0.5ms 273±2ms 1.01 mpas_ocean.CrossSections.time_const_lat('480km', 2)
138±1ms 137±2ms 0.99 mpas_ocean.CrossSections.time_const_lat('480km', 4)
25.0±0.4ms 23.9±0.09ms 0.96 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
2.94±0.2ms 2.79±0.02ms 0.95 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
64.3±1ms 61.2±0.8ms 0.95 mpas_ocean.FaceAreas.time_face_areas('120km')
4.96±5s 7.16±7ms ~0.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.13M 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('120km')
810k 809k 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('480km')
951±7ms 929±6ms 0.98 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
50.5±0.7ms 52.6±0.8ms 1.04 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
85.0±0.5ms 83.3±1ms 0.98 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
4.87±0.2ms 5.03±0.08ms 1.03 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
175±0.5ms 173±0.2ms 0.99 mpas_ocean.Gradient.time_gradient('120km')
12.1±0.03ms 11.9±0.04ms 0.99 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')
4.86M 4.86M 1.00 mpas_ocean.Gradient.track_peakmem_gradient('120km')
313k 313k 1.00 mpas_ocean.Gradient.track_peakmem_gradient('480km')
276±7μs 273±9μs 0.99 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
151±2μs 147±3μs 0.98 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
202±10μs 205±8μs 1.02 mpas_ocean.Integrate.time_integrate('120km')
184±0.7μs 192±5μs 1.04 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')
181±2ms 179±1ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
182±1ms 178±1ms 0.98 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
180±0.7ms 182±1ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
12.8±0.08ms 13.0±0.3ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
12.8±0.05ms 12.9±0.2ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
12.8±0.07ms 12.7±0.03ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
240±1ms 239±0.9ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 1.0)
1.26±0.01s 1.26±0s 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 15.0)
488±1ms 488±3ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('120km', 5.0)
12.8±0.03ms 12.9±0.1ms 1.01 mpas_ocean.NeighborhoodBuild.time_build('480km', 1.0)
24.5±0.2ms 24.4±0.2ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('480km', 15.0)
16.0±0.1ms 16.0±0.1ms 1.00 mpas_ocean.NeighborhoodBuild.time_build('480km', 5.0)
233±2ms 234±1ms 1.01 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 1.0)
1.25±0s 1.24±0.01s 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 15.0)
483±2ms 483±0.4ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('120km', 5.0)
12.6±0.08ms 12.6±0.07ms 1.00 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 1.0)
24.2±0.2ms 23.8±0.02ms 0.99 mpas_ocean.NeighborhoodBuild.time_query_radius('480km', 15.0)
15.6±0.1ms 15.5±0.04ms 0.99 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)
47.5±0.5ms 44.4±0.2ms 0.93 mpas_ocean.NeighborhoodDask.time_mean('120km', 'grid_chunks')
22.5±0.07ms 22.5±0.06ms 1.00 mpas_ocean.NeighborhoodDask.time_mean('120km', 'numpy')
43.6±0.5ms 40.1±0.2ms 0.92 mpas_ocean.NeighborhoodDask.time_mean('120km', 'time_chunks')
697±10μs 691±20μs 0.99 mpas_ocean.NeighborhoodDask.time_mean('480km', 'numpy')
5.84M 5.82M 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.69M 5.67M 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('120km', 'time_chunks')
177k 177k 1.00 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'numpy')
544k 524k 0.96 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'time_chunks')
12.3±0s 12.3±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'mean')
12.9±0s 12.9±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('120km', 'median')
220±2ms 218±1ms 0.99 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'mean')
226±1ms 223±0.5ms 0.99 mpas_ocean.NeighborhoodReduce.time_dataset_reduce('480km', 'median')
1.30±0s 1.30±0.01s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'mean')
1.50±0s 1.50±0s 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('120km', 'median')
24.9±0.02ms 24.8±0.1ms 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'mean')
26.1±0.01ms 26.0±0.08ms 1.00 mpas_ocean.NeighborhoodReduce.time_neighborhood_reduce('480km', 'median')
38.4±0.2ms 38.0±0.07ms 0.99 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'mean')
232±0.6ms 231±0.1ms 1.00 mpas_ocean.NeighborhoodReduce.time_reduce('120km', 'median')
448±10μs 426±8μs 0.95 mpas_ocean.NeighborhoodReduce.time_reduce('480km', 'mean')
1.66±0.01ms 1.62±0.01ms 0.97 mpas_ocean.NeighborhoodReduce.time_reduce('480km', 'median')
240k 239k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('120km', 'mean')
246k 245k 1.00 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('120km', 'median')
20k 19.3k 0.97 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'mean')
20.5k 19.8k 0.97 mpas_ocean.NeighborhoodReduce.track_peakmem_reduce('480km', 'median')
425±10μs 404±10μs 0.95 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
404±10μs 402±10μs 0.99 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
395±20μs 382±10μs 0.97 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')
393±10μs 373±10μs 0.95 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
131±1ms 124±0.8ms 0.95 mpas_ocean.RemapDownsample.time_bilinear_remapping
17.0±0.05ms 16.8±0.01ms 0.99 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
15.4±0.04ms 15.3±0.08ms 0.99 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
1.43±0.02s 1.41±0s 0.98 mpas_ocean.RemapUpsample.time_bilinear_remapping
25.4±0.2ms 25.7±0.2ms 1.01 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
12.4±0.2ms 12.0±0.3ms 0.97 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
8.48±0.08ms 8.38±0.1ms 0.99 mpas_ocean.ZonalAverage.time_zonal_average('120km')
5.35±0.1ms 5.32±0.09ms 0.99 mpas_ocean.ZonalAverage.time_zonal_average('480km')
6.71±0.02ms 6.65±0.02ms 0.99 quad_hexagon.QuadHexagon.time_open_dataset
5.79±0.04ms 5.65±0.02ms 0.98 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.5k 73.6k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_dataset
72.6k 72.7k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_grid

Benchmarks that have got worse:

Change Before [7eff90d] After [0dded6c] Ratio Benchmark (Parameter)
+ 675k 759k 1.12 mpas_ocean.NeighborhoodDask.track_peakmem_mean('480km', 'grid_chunks')

@cmdupuis3

cmdupuis3 commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

In timing the bench_connectivity.Connectivity suite on derecho, I'm finding that with this PR and a full _io_cache, the whole suite takes almost exactly an hour. On the main branch without this PR, just bench_connectivity.Connectivity.time_edge_face and time_face_edge take about 45 minutes each. The Connectivity suite has six tests in total, so the IO cache is actually not being used as extensively as it would be for a full benchmark suite.

asv preimports the benchmark suite before it runs any setup_cache
(asv/runner.py, spawner.preimport() ahead of the run loop), so on a cold
cache bench_connectivity's import-time preload_topologies is what fills
it -- serially, in the forkserver parent, before a single benchmark
starts. CachedFixtures.setup_cache then finds everything already built,
and prime(workers=...) never runs on the path it was written for.

Filling it from the CLI first puts those reads back in the parallel
prime. Worth a second or two on the GitHub runners, which only see the
oQU grids; worth rather more on a machine that can reach the four
dyamond grids on campaign storage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmarking Related to benchmarks, memory usage, and/or time profiling 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.

Need a holistic approach to file I/O for benchmarks

1 participant