diff --git a/geo/CHANGES.md b/geo/CHANGES.md index 7a0b1ab8e..0c36437c8 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -9,6 +9,8 @@ * * Make `SpadeTriangulationConfig` actually configurable * +* PERF: small improvements to TriangulateSpade trait + * ## 0.27.0 diff --git a/geo/Cargo.toml b/geo/Cargo.toml index e587c1299..0096d6fc2 100644 --- a/geo/Cargo.toml +++ b/geo/Cargo.toml @@ -110,3 +110,7 @@ harness = false [[bench]] name = "monotone_subdiv" harness = false + +[[bench]] +name = "triangulate" +harness = false diff --git a/geo/benches/triangulate.rs b/geo/benches/triangulate.rs new file mode 100644 index 000000000..5074589bc --- /dev/null +++ b/geo/benches/triangulate.rs @@ -0,0 +1,81 @@ +use criterion::{criterion_group, criterion_main}; +use geo::algorithm::{TriangulateEarcut, TriangulateSpade}; +use geo::geometry::Polygon; +use geo::triangulate_spade::SpadeTriangulationConfig; + +fn criterion_benchmark(c: &mut criterion::Criterion) { + c.bench_function( + "TriangulateSpade (unconstrained) - small polys", + |bencher| { + let multi_poly = geo_test_fixtures::nl_zones::(); + bencher.iter(|| { + for poly in &multi_poly { + let triangulation = + TriangulateSpade::unconstrained_triangulation(poly).unwrap(); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + } + }); + }, + ); + + c.bench_function("TriangulateSpade (constrained) - small polys", |bencher| { + let multi_poly = geo_test_fixtures::nl_zones::(); + bencher.iter(|| { + for poly in &multi_poly { + let triangulation = TriangulateSpade::constrained_triangulation( + poly, + SpadeTriangulationConfig { snap_radius: 1e-8 }, + ) + .unwrap(); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + } + }); + }); + + c.bench_function("TriangulateEarcut - small polys", |bencher| { + let multi_poly = geo_test_fixtures::nl_zones::(); + bencher.iter(|| { + for poly in &multi_poly { + let triangulation = TriangulateEarcut::earcut_triangles(poly); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + } + }); + }); + + c.bench_function("TriangulateSpade (unconstrained) - large_poly", |bencher| { + let poly = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + bencher.iter(|| { + let triangulation = TriangulateSpade::unconstrained_triangulation(&poly).unwrap(); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + }); + }); + + c.bench_function("TriangulateSpade (constrained) - large_poly", |bencher| { + let poly = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + bencher.iter(|| { + let triangulation = TriangulateSpade::constrained_triangulation( + &poly, + SpadeTriangulationConfig { snap_radius: 1e-8 }, + ) + .unwrap(); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + }); + }); + + c.bench_function("TriangulateEarcut - large_poly", |bencher| { + let poly = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + bencher.iter(|| { + let triangulation = TriangulateEarcut::earcut_triangles(&poly); + assert!(triangulation.len() > 1); + criterion::black_box(triangulation); + }); + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/geo/src/algorithm/triangulate_spade.rs b/geo/src/algorithm/triangulate_spade.rs index 544c53963..96094a900 100644 --- a/geo/src/algorithm/triangulate_spade.rs +++ b/geo/src/algorithm/triangulate_spade.rs @@ -65,6 +65,9 @@ pub type Triangles = Vec>; // so that we don't leak these weird methods on the public interface. mod private { use super::*; + + pub(crate) type CoordsIter<'a, T> = Box> + 'a>; + pub trait TriangulationRequirementTrait<'a, T> where T: SpadeTriangulationFloat, @@ -76,7 +79,7 @@ mod private { fn lines(&'a self) -> Vec>; /// collect all the coords that are relevant for triangulations from the geometric object that /// should be triangulated - fn coords(&'a self) -> Vec>; + fn coords(&'a self) -> CoordsIter; /// define a predicate that decides if a point is inside of the object (used for constrained triangulation) fn contains_point(&'a self, p: Point) -> bool; @@ -315,8 +318,8 @@ where T: SpadeTriangulationFloat, G: LinesIter<'l, Scalar = T> + CoordsIter + Contains>, { - fn coords(&'a self) -> Vec> { - self.coords_iter().collect::>() + fn coords(&'a self) -> private::CoordsIter { + Box::new(self.coords_iter()) } fn lines(&'a self) -> Vec> { @@ -333,11 +336,11 @@ where impl<'a, T, G> private::TriangulationRequirementTrait<'a, T> for Vec where - T: SpadeTriangulationFloat, + T: SpadeTriangulationFloat + 'a, G: TriangulateSpade<'a, T>, { - fn coords(&'a self) -> Vec> { - self.iter().flat_map(|g| g.coords()).collect::>() + fn coords(&'a self) -> private::CoordsIter { + Box::new(self.iter().flat_map(|g| g.coords())) } fn lines(&'a self) -> Vec> { @@ -351,11 +354,11 @@ where impl<'a, T, G> private::TriangulationRequirementTrait<'a, T> for &[G] where - T: SpadeTriangulationFloat, + T: SpadeTriangulationFloat + 'a, G: TriangulateSpade<'a, T>, { - fn coords(&'a self) -> Vec> { - self.iter().flat_map(|g| g.coords()).collect::>() + fn coords(&'a self) -> private::CoordsIter { + Box::new(self.iter().flat_map(|g| g.coords())) } fn lines(&'a self) -> Vec> {