@@ -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 >
47105where
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