Skip to content

Commit de6a353

Browse files
Re-introduce safe implementation (#481)
1 parent 1eb052c commit de6a353

File tree

1 file changed

+51
-67
lines changed

1 file changed

+51
-67
lines changed

src/consolidation.rs

Lines changed: 51 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -37,49 +37,40 @@ pub fn consolidate_from<T: Ord, R: Semigroup>(vec: &mut Vec<(T, R)>, offset: usi
3737
/// Sorts and consolidates a slice, returning the valid prefix length.
3838
pub fn consolidate_slice<T: Ord, R: Semigroup>(slice: &mut [(T, R)]) -> usize {
3939

40-
// We could do an insertion-sort like initial scan which builds up sorted, consolidated runs.
41-
// In a world where there are not many results, we may never even need to call in to merge sort.
42-
slice.sort_by(|x,y| x.0.cmp(&y.0));
43-
44-
let slice_ptr = slice.as_mut_ptr();
45-
46-
// Counts the number of distinct known-non-zero accumulations. Indexes the write location.
47-
let mut offset = 0;
48-
for index in 1 .. slice.len() {
49-
50-
// The following unsafe block elides various bounds checks, using the reasoning that `offset`
51-
// is always strictly less than `index` at the beginning of each iteration. This is initially
52-
// true, and in each iteration `offset` can increase by at most one (whereas `index` always
53-
// increases by one). As `index` is always in bounds, and `offset` starts at zero, it too is
54-
// always in bounds.
55-
//
56-
// LLVM appears to struggle to optimize out Rust's split_at_mut, which would prove disjointness
57-
// using run-time tests.
58-
unsafe {
59-
60-
assert!(offset < index);
61-
62-
// LOOP INVARIANT: offset < index
63-
let ptr1 = slice_ptr.add(offset);
64-
let ptr2 = slice_ptr.add(index);
65-
66-
if (*ptr1).0 == (*ptr2).0 {
67-
(*ptr1).1.plus_equals(&(*ptr2).1);
40+
if slice.len() > 1 {
41+
42+
// We could do an insertion-sort like initial scan which builds up sorted, consolidated runs.
43+
// In a world where there are not many results, we may never even need to call in to merge sort.
44+
slice.sort_by(|x,y| x.0.cmp(&y.0));
45+
46+
// Counts the number of distinct known-non-zero accumulations. Indexes the write location.
47+
let mut offset = 0;
48+
let mut accum = slice[offset].1.clone();
49+
50+
for index in 1 .. slice.len() {
51+
if slice[index].0 == slice[index-1].0 {
52+
accum.plus_equals(&slice[index].1);
6853
}
6954
else {
70-
if !(*ptr1).1.is_zero() {
55+
if !accum.is_zero() {
56+
slice.swap(offset, index-1);
57+
slice[offset].1.clone_from(&accum);
7158
offset += 1;
7259
}
73-
let ptr1 = slice_ptr.add(offset);
74-
std::ptr::swap(ptr1, ptr2);
60+
accum.clone_from(&slice[index].1);
7561
}
7662
}
63+
if !accum.is_zero() {
64+
slice.swap(offset, slice.len()-1);
65+
slice[offset].1 = accum;
66+
offset += 1;
67+
}
68+
69+
offset
7770
}
78-
if offset < slice.len() && !slice[offset].1.is_zero() {
79-
offset += 1;
71+
else {
72+
slice.iter().filter(|x| !x.1.is_zero()).count()
8073
}
81-
82-
offset
8374
}
8475

8576
/// Sorts and consolidates `vec`.
@@ -104,50 +95,43 @@ pub fn consolidate_updates_from<D: Ord, T: Ord, R: Semigroup>(vec: &mut Vec<(D,
10495
/// Sorts and consolidates a slice, returning the valid prefix length.
10596
pub fn consolidate_updates_slice<D: Ord, T: Ord, R: Semigroup>(slice: &mut [(D, T, R)]) -> usize {
10697

107-
// We could do an insertion-sort like initial scan which builds up sorted, consolidated runs.
108-
// In a world where there are not many results, we may never even need to call in to merge sort.
109-
slice.sort_unstable_by(|x,y| (&x.0, &x.1).cmp(&(&y.0, &y.1)));
110-
111-
let slice_ptr = slice.as_mut_ptr();
98+
if slice.len() > 1 {
11299

113-
// Counts the number of distinct known-non-zero accumulations. Indexes the write location.
114-
let mut offset = 0;
115-
for index in 1 .. slice.len() {
100+
// We could do an insertion-sort like initial scan which builds up sorted, consolidated runs.
101+
// In a world where there are not many results, we may never even need to call in to merge sort.
102+
slice.sort_unstable_by(|x,y| (&x.0, &x.1).cmp(&(&y.0, &y.1)));
116103

117-
// The following unsafe block elides various bounds checks, using the reasoning that `offset`
118-
// is always strictly less than `index` at the beginning of each iteration. This is initially
119-
// true, and in each iteration `offset` can increase by at most one (whereas `index` always
120-
// increases by one). As `index` is always in bounds, and `offset` starts at zero, it too is
121-
// always in bounds.
122-
//
123-
// LLVM appears to struggle to optimize out Rust's split_at_mut, which would prove disjointness
124-
// using run-time tests.
125-
unsafe {
104+
// Counts the number of distinct known-non-zero accumulations. Indexes the write location.
105+
let mut offset = 0;
106+
let mut accum = slice[offset].2.clone();
126107

127-
// LOOP INVARIANT: offset < index
128-
let ptr1 = slice_ptr.add(offset);
129-
let ptr2 = slice_ptr.add(index);
130-
131-
if (*ptr1).0 == (*ptr2).0 && (*ptr1).1 == (*ptr2).1 {
132-
(*ptr1).2.plus_equals(&(*ptr2).2);
108+
for index in 1 .. slice.len() {
109+
if (slice[index].0 == slice[index-1].0) && (slice[index].1 == slice[index-1].1) {
110+
accum.plus_equals(&slice[index].2);
133111
}
134112
else {
135-
if !(*ptr1).2.is_zero() {
113+
if !accum.is_zero() {
114+
slice.swap(offset, index-1);
115+
slice[offset].2.clone_from(&accum);
136116
offset += 1;
137117
}
138-
let ptr1 = slice_ptr.add(offset);
139-
std::ptr::swap(ptr1, ptr2);
118+
accum.clone_from(&slice[index].2);
140119
}
141-
142120
}
121+
if !accum.is_zero() {
122+
slice.swap(offset, slice.len()-1);
123+
slice[offset].2 = accum;
124+
offset += 1;
125+
}
126+
127+
offset
143128
}
144-
if offset < slice.len() && !slice[offset].2.is_zero() {
145-
offset += 1;
129+
else {
130+
slice.iter().filter(|x| !x.2.is_zero()).count()
146131
}
147-
148-
offset
149132
}
150133

134+
151135
/// A container builder that consolidates data in-places into fixed-sized containers. Does not
152136
/// maintain FIFO ordering.
153137
#[derive(Default)]
@@ -332,6 +316,6 @@ mod tests {
332316
for i in 0..1024 {
333317
assert_eq!((i, 0, 2), collected[i]);
334318
}
335-
319+
336320
}
337321
}

0 commit comments

Comments
 (0)