Skip to content

Commit

Permalink
Simplify tree traversal algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
urschrei committed Nov 5, 2024
1 parent 608244c commit 537b664
Showing 1 changed file with 37 additions and 63 deletions.
100 changes: 37 additions & 63 deletions geo/src/algorithm/bool_ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I, F, R> {
init: I,
fold: F,
reduce: R,
}

/// Efficient [BooleanOps::union] of adjacent / overlapping geometries
///
/// For geometries with a high degree of overlap or adjacency
Expand Down Expand Up @@ -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<T, S, I, F, R>(tree: &RTree<T>, init: I, fold: F, reduce: R) -> S
fn bottom_up_fold_reduce<T, S, I, F, R>(ops: &mut Ops<I, F, R>, parent: &ParentNode<T>) -> 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<I, F, R> {
init: I,
fold: F,
reduce: R,
}

fn inner<T, S, I, F, R>(ops: &mut Ops<I, F, R>, parent: &ParentNode<T>) -> 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<T, S, I, F, R>(tree: &RTree<T>, init: I, fold: F, reduce: R) -> S
fn par_bottom_up_fold_reduce<T, S, I, F, R>(ops: &Ops<I, F, R>, parent: &ParentNode<T>) -> S
where
T: RTreeObject,
RTreeNode<T>: Send + Sync,
Expand All @@ -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<I, F, R> {
init: I,
fold: F,
reduce: R,
}

fn inner<T, S, I, F, R>(ops: &Ops<I, F, R>, parent: &ParentNode<T>) -> S
where
T: RTreeObject,
RTreeNode<T>: 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<T: BoolOpsNum> BooleanOps for Polygon<T> {
Expand Down Expand Up @@ -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())
}
}

Expand Down Expand Up @@ -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())
}
}

0 comments on commit 537b664

Please sign in to comment.