From 58d6bac76736d9df10c38a05345a218574d6c513 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 5 Dec 2023 08:53:57 -0800 Subject: [PATCH 1/2] benchmarks for triangulate --- geo/Cargo.toml | 4 ++ geo/benches/triangulate.rs | 81 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 geo/benches/triangulate.rs 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); From aa71ffd512a2f7334b56e075a1bdb33aa1044bdb Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 5 Dec 2023 08:37:50 -0800 Subject: [PATCH 2/2] PERF: `TriangulateSpade` uses Iter, not Vec This is an internal API, so we can safely break the API. Bench output: ``` TriangulateSpade (unconstrained) - small polys time: [8.7534 ms 8.7641 ms 8.7777 ms] change: [-2.6587% -2.1965% -1.7360%] (p = 0.00 < 0.05) Performance has improved. Found 5 outliers among 100 measurements (5.00%) 3 (3.00%) high mild 2 (2.00%) high severe Benchmarking TriangulateSpade (constrained) - small polys: Warming up for 3.0000 s Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 98.7s, or reduce sample count to 10. TriangulateSpade (constrained) - small polys time: [986.61 ms 987.03 ms 987.51 ms] change: [-5.2413% -5.0808% -4.9190%] (p = 0.00 < 0.05) Performance has improved. Found 17 outliers among 100 measurements (17.00%) 7 (7.00%) high mild 10 (10.00%) high severe TriangulateEarcut - small polys time: [6.9938 ms 6.9993 ms 7.0041 ms] change: [-1.3566% -0.9176% -0.5312%] (p = 0.00 < 0.05) Change within noise threshold. Found 12 outliers among 100 measurements (12.00%) 5 (5.00%) low severe 5 (5.00%) low mild 2 (2.00%) high mild TriangulateSpade (unconstrained) - large_poly time: [3.4280 ms 3.4390 ms 3.4521 ms] change: [+0.2922% +0.7947% +1.3032%] (p = 0.00 < 0.05) Change within noise threshold. Found 13 outliers among 100 measurements (13.00%) 7 (7.00%) high mild 6 (6.00%) high severe Benchmarking TriangulateSpade (constrained) - large_poly: Warming up for 3.0000 s Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 178.3s, or reduce sample count to 10. TriangulateSpade (constrained) - large_poly time: [1.7869 s 1.8005 s 1.8178 s] change: [-7.9992% -5.8498% -4.2765%] (p = 0.00 < 0.05) Performance has improved. Found 14 outliers among 100 measurements (14.00%) 5 (5.00%) high mild 9 (9.00%) high severe TriangulateEarcut - large_poly time: [2.2287 ms 2.2346 ms 2.2413 ms] change: [+1.0859% +1.4903% +1.9614%] (p = 0.00 < 0.05) Performance has regressed. Found 14 outliers among 100 measurements (14.00%) 10 (10.00%) high mild 4 (4.00%) high severe ``` --- geo/CHANGES.md | 2 ++ geo/src/algorithm/triangulate_spade.rs | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) 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/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> {