Skip to content

Commit 74c228f

Browse files
committed
rcb: avoid re-computations of min/max/sum
instead, keep track of the bounding boxes and the sum of the parts. This reduces the load of high-iter-count runs.
1 parent 19815e6 commit 74c228f

File tree

1 file changed

+57
-21
lines changed

1 file changed

+57
-21
lines changed

src/algorithms/recursive_bisection.rs

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::Error;
22
use crate::geometry::OrientedBoundingBox;
33
use crate::geometry::PointND;
4+
use crate::BoundingBox;
45
use nalgebra::allocator::Allocator;
56
use nalgebra::ArrayStorage;
67
use nalgebra::Const;
@@ -25,17 +26,30 @@ struct Item<'p, const D: usize, W> {
2526
part: &'p AtomicUsize,
2627
}
2728

29+
/// Return value of [rcb_split].
30+
struct SplitResult<W> {
31+
/// Index of the first item in the right part in the array of [Item]s.
32+
split_idx: usize,
33+
/// Weight of the left part, used to compute the sum for the next iteration.
34+
weight_left: W,
35+
/// Coordinate value of the split, used to compute the [BoundingBox]es.
36+
split_pos: f64,
37+
}
38+
2839
fn rcb_split<const D: usize, W>(
2940
items: &mut [Item<D, W>],
3041
coord: usize,
3142
tolerance: f64,
3243
mut min: f64,
3344
mut max: f64,
3445
sum: W,
35-
) -> usize
46+
) -> SplitResult<W>
3647
where
3748
W: RcbWeight,
3849
{
50+
let span = tracing::info_span!("rcb_split");
51+
let _enter = span.enter();
52+
3953
let mut prev_count_left = usize::MAX;
4054
loop {
4155
let split_target = (min + max) / 2.0;
@@ -57,7 +71,11 @@ where
5771
f64::abs((weight_left - ideal_weight_left) / ideal_weight_left)
5872
};
5973
if count_left == prev_count_left || imbalance < tolerance {
60-
return count_left;
74+
return SplitResult {
75+
split_idx: count_left,
76+
weight_left,
77+
split_pos: split_target,
78+
};
6179
}
6280
prev_count_left = count_left;
6381

@@ -76,6 +94,8 @@ fn rcb_recurse<const D: usize, W>(
7694
iter_id: usize,
7795
coord: usize,
7896
tolerance: f64,
97+
sum: W,
98+
bb: BoundingBox<D>,
7999
) where
80100
W: RcbWeight,
81101
{
@@ -84,33 +104,35 @@ fn rcb_recurse<const D: usize, W>(
84104
return;
85105
}
86106
if iter_count == 0 {
107+
let span = tracing::info_span!(
108+
"rcb_recurse: apply_part_id",
109+
item_count = items.len(),
110+
iter_id,
111+
);
112+
let _enter = span.enter();
113+
87114
items
88115
.into_par_iter()
89116
.for_each(|item| item.part.store(iter_id, Ordering::Relaxed));
90117
return;
91118
}
92119

93-
let sum: W = items.par_iter().map(|item| item.weight).sum();
94-
let (min, max) = items
95-
.par_iter()
96-
.fold(
97-
|| (f64::INFINITY, f64::NEG_INFINITY),
98-
|(min, max), item| {
99-
(
100-
f64::min(min, item.point[coord]),
101-
f64::max(max, item.point[coord]),
102-
)
103-
},
104-
)
105-
.reduce(
106-
|| (f64::INFINITY, f64::NEG_INFINITY),
107-
|(min0, max0), (min1, max1)| (f64::min(min0, min1), f64::max(max0, max1)),
108-
);
120+
let span = tracing::info_span!("rcb_recurse");
121+
let enter = span.enter();
109122

110-
let split_idx = rcb_split(items, coord, tolerance, min, max, sum);
123+
let min = bb.p_min[coord];
124+
let max = bb.p_max[coord];
125+
let SplitResult {
126+
split_idx,
127+
weight_left,
128+
split_pos,
129+
} = rcb_split(items, coord, tolerance, min, max, sum);
111130
let (left, right) = if split_idx == items.len() {
112131
items.split_at_mut(items.len())
113132
} else {
133+
let span = tracing::info_span!("select_nth_unstable");
134+
let _enter = span.enter();
135+
114136
let (left, _, _right_minus_one) = items
115137
.select_nth_unstable_by(split_idx, |item1, item2| {
116138
f64::partial_cmp(&item1.point[coord], &item2.point[coord]).unwrap()
@@ -119,6 +141,13 @@ fn rcb_recurse<const D: usize, W>(
119141
items.split_at_mut(left_len)
120142
};
121143

144+
let mut bb_left = bb.clone();
145+
bb_left.p_max[coord] = split_pos;
146+
let mut bb_right = bb;
147+
bb_right.p_min[coord] = split_pos;
148+
149+
mem::drop(enter);
150+
122151
rayon::join(
123152
|| {
124153
rcb_recurse(
@@ -127,6 +156,8 @@ fn rcb_recurse<const D: usize, W>(
127156
2 * iter_id + 1,
128157
(coord + 1) % D,
129158
tolerance,
159+
weight_left,
160+
bb_left,
130161
)
131162
},
132163
|| {
@@ -136,6 +167,8 @@ fn rcb_recurse<const D: usize, W>(
136167
2 * iter_id + 2,
137168
(coord + 1) % D,
138169
tolerance,
170+
sum - weight_left,
171+
bb_right,
139172
)
140173
},
141174
);
@@ -171,7 +204,7 @@ where
171204
});
172205
}
173206
if partition.is_empty() {
174-
// Would make the partition.min() at the end panic.
207+
// Would make BoundingBox::from_points and partition.min() panic.
175208
return Ok(());
176209
}
177210

@@ -184,9 +217,12 @@ where
184217
part,
185218
})
186219
.collect();
220+
let sum = items.par_iter().map(|item| item.weight).sum();
221+
let bb = BoundingBox::from_points(items.par_iter().map(|item| item.point)).unwrap();
187222

188-
rcb_recurse(&mut items, iter_count, 0, 0, tolerance);
223+
rcb_recurse(&mut items, iter_count, 0, 0, tolerance, sum, bb);
189224

225+
// Part IDs must start from zero.
190226
let part_id_offset = *partition.par_iter().min().unwrap();
191227
partition
192228
.par_iter_mut()

0 commit comments

Comments
 (0)