diff --git a/geo/src/algorithm/bool_ops/mod.rs b/geo/src/algorithm/bool_ops/mod.rs index 9d04bf122..80ee01b34 100644 --- a/geo/src/algorithm/bool_ops/mod.rs +++ b/geo/src/algorithm/bool_ops/mod.rs @@ -137,6 +137,16 @@ pub enum OpType { Xor, } +// Recursive algorithms can benefit from grouping those parameters which are constant over +// the whole algorithm to reduce the overhead of the recursive calls, in this case the single- +// and multi-threaded unary union tree traversals +#[derive(Debug)] +struct Ops { + init: I, + fold: F, + reduce: R, +} + /// Efficient [BooleanOps::union] of adjacent / overlapping geometries /// /// For geometries with a high degree of overlap or adjacency @@ -180,44 +190,26 @@ pub trait UnaryUnion { // Though the operation is carried out via fold() over the tree iterator, there are two actual nested operations: // "fold" operations on leaf nodes build up output MultiPolygons by adding Polygons to them via union and // "reduce" operations on parent nodes combine these output MultiPolygons from leaf operations by recursion -fn bottom_up_fold_reduce(tree: &RTree, init: I, fold: F, reduce: R) -> S +fn bottom_up_fold_reduce(ops: &mut Ops, parent: &ParentNode) -> S where T: RTreeObject, I: FnMut() -> S, F: FnMut(S, &T) -> S, R: FnMut(S, S) -> S, { - // recursive algorithms can benefit from grouping those parameters which are constant over - // the whole algorithm to reduce the overhead of the recursive calls - struct Ops { - init: I, - fold: F, - reduce: R, - } - - fn inner(ops: &mut Ops, parent: &ParentNode) -> S - where - T: RTreeObject, - I: FnMut() -> S, - F: FnMut(S, &T) -> S, - R: FnMut(S, S) -> S, - { - parent - .children() - .iter() - .fold((ops.init)(), |accum, child| match child { - RTreeNode::Leaf(value) => (ops.fold)(accum, value), - RTreeNode::Parent(parent) => { - let value = inner(ops, parent); - (ops.reduce)(accum, value) - } - }) - } - let mut ops = Ops { init, fold, reduce }; - inner(&mut ops, tree.root()) + parent + .children() + .iter() + .fold((ops.init)(), |accum, child| match child { + RTreeNode::Leaf(value) => (ops.fold)(accum, value), + RTreeNode::Parent(parent) => { + let value = bottom_up_fold_reduce(ops, parent); + (ops.reduce)(accum, value) + } + }) } -fn par_bottom_up_fold_reduce(tree: &RTree, init: I, fold: F, reduce: R) -> S +fn par_bottom_up_fold_reduce(ops: &Ops, parent: &ParentNode) -> S where T: RTreeObject, RTreeNode: Send + Sync, @@ -226,37 +218,17 @@ where F: Fn(S, &T) -> S + Send + Sync, R: Fn(S, S) -> S + Send + Sync, { - // recursive algorithms can benefit from grouping those parameters which are constant over - // the whole algorithm to reduce the overhead of the recursive calls - struct Ops { - init: I, - fold: F, - reduce: R, - } - - fn inner(ops: &Ops, parent: &ParentNode) -> S - where - T: RTreeObject, - RTreeNode: Send + Sync, - S: Send, - I: Fn() -> S + Send + Sync, - F: Fn(S, &T) -> S + Send + Sync, - R: Fn(S, S) -> S + Send + Sync, - { - parent - .children() - .into_par_iter() - .fold(&ops.init, |accum, child| match child { - RTreeNode::Leaf(value) => (ops.fold)(accum, value), - RTreeNode::Parent(parent) => { - let value = inner(ops, parent); - (ops.reduce)(accum, value) - } - }) - .reduce(&ops.init, &ops.reduce) - } - let ops = Ops { init, fold, reduce }; - inner(&ops, tree.root()) + parent + .children() + .into_par_iter() + .fold(&ops.init, |accum, child| match child { + RTreeNode::Leaf(value) => (ops.fold)(accum, value), + RTreeNode::Parent(parent) => { + let value = par_bottom_up_fold_reduce(ops, parent); + (ops.reduce)(accum, value) + } + }) + .reduce(&ops.init, &ops.reduce) } impl BooleanOps for Polygon { @@ -300,7 +272,8 @@ where .map(|p| CachedEnvelope::new(ObjectRef::new(p))) .collect(), ); - bottom_up_fold_reduce(&rtree, init, fold, reduce) + let mut ops = Ops { init, fold, reduce }; + bottom_up_fold_reduce(&mut ops, rtree.root()) } } @@ -337,6 +310,7 @@ where .collect(), ); - par_bottom_up_fold_reduce(&rtree, init, fold, reduce) + let ops = Ops { init, fold, reduce }; + par_bottom_up_fold_reduce(&ops, rtree.root()) } }