Skip to content

Commit 8bd9588

Browse files
committed
rcb: don't split small item sets in parallel
It's slower to go through rayon than to sort+split them.
1 parent 214ad3b commit 8bd9588

File tree

1 file changed

+76
-27
lines changed

1 file changed

+76
-27
lines changed

src/algorithms/recursive_bisection.rs

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,86 @@ struct Item<'p, const D: usize, W> {
2626
part: &'p AtomicUsize,
2727
}
2828

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,
29+
/// Return value of [rcb_split] and [par_rcb_split].
30+
struct SplitResult<'a, 'p, const D: usize, W> {
31+
left: &'a mut [Item<'p, D, W>],
32+
right: &'a mut [Item<'p, D, W>],
3333
/// Weight of the left part, used to compute the sum for the next iteration.
3434
weight_left: W,
3535
/// Coordinate value of the split, used to compute the [BoundingBox]es.
3636
split_pos: f64,
3737
}
3838

39-
fn rcb_split<const D: usize, W>(
40-
items: &mut [Item<D, W>],
39+
/// Splits the given items into two sets of similar weights.
40+
fn rcb_split<'a, 'p, const D: usize, W>(
41+
items: &'a mut [Item<'p, D, W>],
42+
coord: usize,
43+
max: f64,
44+
sum: W,
45+
) -> SplitResult<'a, 'p, D, W>
46+
where
47+
W: RcbWeight,
48+
{
49+
let span = tracing::info_span!("rcb_split");
50+
let _enter = span.enter();
51+
52+
items.sort_unstable_by(|item1, item2| {
53+
crate::partial_cmp(&item1.point[coord], &item2.point[coord])
54+
});
55+
56+
let mut count_left = items.len();
57+
let mut split_pos = max;
58+
let mut weight_left = W::default();
59+
for (i, item) in items.iter().enumerate() {
60+
if sum <= weight_left + weight_left {
61+
count_left = i;
62+
split_pos = item.point[coord];
63+
break;
64+
}
65+
weight_left += item.weight;
66+
}
67+
68+
let (left, right) = items.split_at_mut(count_left);
69+
70+
SplitResult {
71+
left,
72+
right,
73+
weight_left,
74+
split_pos,
75+
}
76+
}
77+
78+
/// Wrapper around [`slice::split_at_mut`] which makes it so elements of the
79+
/// left side are all lower than all elements of the right side.
80+
fn reorder_split<F, T>(s: &mut [T], index: usize, compare: F) -> (&mut [T], &mut [T])
81+
where
82+
F: Fn(&T, &T) -> cmp::Ordering,
83+
{
84+
if index == s.len() {
85+
s.split_at_mut(s.len())
86+
} else {
87+
let span = tracing::info_span!("select_nth_unstable");
88+
let _enter = span.enter();
89+
90+
let (left, _, _right_minus_one) = s.select_nth_unstable_by(index, compare);
91+
let left_len = left.len();
92+
s.split_at_mut(left_len)
93+
}
94+
}
95+
96+
/// Splits the given items into two sets of similar weights (parallel version).
97+
fn par_rcb_split<'a, 'p, const D: usize, W>(
98+
items: &'a mut [Item<'p, D, W>],
4199
coord: usize,
42100
tolerance: f64,
43101
mut min: f64,
44102
mut max: f64,
45103
sum: W,
46-
) -> SplitResult<W>
104+
) -> SplitResult<'a, 'p, D, W>
47105
where
48106
W: RcbWeight,
49107
{
50-
let span = tracing::info_span!("rcb_split");
108+
let span = tracing::info_span!("par_rcb_split");
51109
let _enter = span.enter();
52110

53111
let mut prev_count_left = usize::MAX;
@@ -71,8 +129,12 @@ where
71129
f64::abs((weight_left - ideal_weight_left) / ideal_weight_left)
72130
};
73131
if count_left == prev_count_left || imbalance < tolerance {
132+
let (left, right) = reorder_split(items, count_left, |item1, item2| {
133+
crate::partial_cmp(&item1.point[coord], &item2.point[coord])
134+
});
74135
return SplitResult {
75-
split_idx: count_left,
136+
left,
137+
right,
76138
weight_left,
77139
split_pos: split_target,
78140
};
@@ -117,37 +179,24 @@ fn rcb_recurse<const D: usize, W>(
117179
return;
118180
}
119181

120-
let span = tracing::info_span!("rcb_recurse");
121-
let enter = span.enter();
122-
123182
let min = bb.p_min[coord];
124183
let max = bb.p_max[coord];
125184
let SplitResult {
126-
split_idx,
185+
left,
186+
right,
127187
weight_left,
128188
split_pos,
129-
} = rcb_split(items, coord, tolerance, min, max, sum);
130-
let (left, right) = if split_idx == items.len() {
131-
items.split_at_mut(items.len())
189+
} = if items.len() > 4096 {
190+
par_rcb_split(items, coord, tolerance, min, max, sum)
132191
} else {
133-
let span = tracing::info_span!("select_nth_unstable");
134-
let _enter = span.enter();
135-
136-
let (left, _, _right_minus_one) = items
137-
.select_nth_unstable_by(split_idx, |item1, item2| {
138-
crate::partial_cmp(&item1.point[coord], &item2.point[coord])
139-
});
140-
let left_len = left.len();
141-
items.split_at_mut(left_len)
192+
rcb_split(items, coord, max, sum)
142193
};
143194

144195
let mut bb_left = bb.clone();
145196
bb_left.p_max[coord] = split_pos;
146197
let mut bb_right = bb;
147198
bb_right.p_min[coord] = split_pos;
148199

149-
mem::drop(enter);
150-
151200
rayon::join(
152201
|| {
153202
rcb_recurse(

0 commit comments

Comments
 (0)