Skip to content

Benchmarks

Note

All benchmark values are taken from the examples/ folder and the included Jupyter Notebooks.

All benchmarks compare extractrs against exactextract, the reference implementation for exact zonal statistics.

CONUS-Scale Benchmark

Dataset: Daymet daily minimum temperature (2024), 1km Lambert Conformal Conic grid, 7814 x 8075 cells (~63M cells).

Polygons: 225,064 MERIT Pfafstetter level-7 sub-basins covering the contiguous United States.

Python Package

Median of 10 runs for extractrs (cache pre-built) vs. median of 3 runs for exactextract (fewer runs due to the longer runtime):

Step extractrs exactextract
Cache build (one-time) 8,181 ms
Per-timestep stat 166 ms 138,392 ms
Single-timestep total 8,347 ms 138,392 ms

Single timestep: 17x speedup. For multi-timestep workflows, the cache cost amortizes away.

365 daily timesteps (1 year): extractrs total = 8.2s + 365 x 0.17s = 69s. exactextract total = 365 x 138s = 14 hours. Speedup: 733x.

Rust Pipeline

Step Time
Basin read (shapefile, filtered to CONUS bounding box) 2.4 s
Raster read (14 GB NetCDF) 4.9 s
Coverage cache build (one-time) 12.0 s
Per-day zonal stats 0.125 s
exactextract per-day equivalent 140.4 s

The Python package per-timestep time (166ms) is higher than the Rust pipeline (125ms) due to numpy array marshaling overhead across the PyO3 boundary.

Why the Cache Matters

exactextract recomputes polygon-cell intersections on every call. extractrs separates the intersection step (cache build) from the aggregation step (apply stat). For multi-timestep workflows — processing a year of daily data across thousands of basins — the intersection geometry never changes. Computing it once and reusing it eliminates redundant work.

Accuracy

extractrs uses the same polygon-cell intersection algorithm as exactextract. On a validation subset of 82 CONUS basins (spanning diverse sizes from small headwater catchments to large river basins):

  • Pearson R = 1.0
  • Maximum absolute difference = 0.0°C (bit-identical results)

At full CONUS scale (225,064 basins), the Rust pipeline achieves:

  • R = 0.9999999999

The deviation at scale comes from floating-point accumulation order differences between the two implementations, not algorithmic differences. Both implementations compute the same polygon-cell intersections — the only variation is the order in which partial-cell contributions are summed, which affects the least significant bits of the result.

Environment

  • CPU: Single core — no parallelism in the stat application step. The cache build is also single-threaded.
  • extractrs version: 0.1.0
  • exactextract version: 0.2.0 (PyPI)
  • Python: 3.13
  • Dependencies: numpy, xarray, geopandas