Skip to content

Commit 084911a

Browse files
Improve performance of Relation::merge
Instead of doing the easy thing and just doing a sort-dedup, we make the following optimizations: - if either element list is empty, just return that one - if the first element of one list is > the last element of the other list, just extend the storage for the other list and append. - perform a proper zipper merge of the two lists.
1 parent 111e8d2 commit 084911a

File tree

1 file changed

+67
-17
lines changed

1 file changed

+67
-17
lines changed

src/lib.rs

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,77 @@ impl<Tuple: Ord> Relation<Tuple> {
3434
let mut elements1 = self.elements;
3535
let mut elements2 = other.elements;
3636

37-
// Ensure elements1.cap() >= elements2.cap().
38-
if elements1.capacity() < elements2.capacity() {
39-
::std::mem::swap(&mut elements1, &mut elements2);
37+
// If one of the element lists is zero-length, we don't need to do any work
38+
if elements1.len() == 0 {
39+
return Relation { elements: elements2 };
40+
}
41+
if elements2.len() == 0 {
42+
return Relation { elements: elements1 };
43+
}
44+
45+
// Make sure that elements1 starts with the lower element
46+
// Will not panic since both collections must have at least 1 element at this point
47+
if elements1[0] > elements2[0] {
48+
std::mem::swap(&mut elements1, &mut elements2);
4049
}
4150

42-
// Merge results either in spare capacity or new vector.
43-
let mut elements =
44-
if elements1.len() + elements2.len() < elements1.capacity() {
51+
// Fast path for when all the new elements are after the exiting ones
52+
if elements1[elements1.len() - 1] < elements2[0] {
4553
elements1.extend(elements2.into_iter());
46-
elements1
54+
// println!("fast path");
55+
return Relation { elements: elements1 };
4756
}
48-
else {
49-
let mut vec = Vec::with_capacity(elements1.len() + elements2.len());
50-
vec.extend(elements1.into_iter());
51-
vec.extend(elements2.into_iter());
52-
vec
53-
};
54-
55-
// Sort, dedup, and return.
56-
elements.sort();
57-
elements.dedup();
57+
58+
let mut elements = Vec::with_capacity(elements1.len() + elements2.len());
59+
let mut elements1 = elements1.drain(..);
60+
let mut elements2 = elements2.drain(..).peekable();
61+
// let mut from_1 = 0;
62+
// let mut from_2 = 0;
63+
64+
elements.push(elements1.next().unwrap());
65+
// from_1 += 1;
66+
67+
for elem in elements1 {
68+
loop {
69+
// Pull everything out of elements2 that is less than the thing
70+
// we're about to insert into elements from elements1
71+
use std::cmp::Ordering;
72+
let cmpres = match elements2.peek() {
73+
None => Ordering::Greater,
74+
Some(e2) => e2.cmp(&elem)
75+
};
76+
77+
match cmpres {
78+
Ordering::Greater => break,
79+
Ordering::Equal => {
80+
// consume without pushing
81+
elements2.next();
82+
},
83+
Ordering::Less => {
84+
let tp = elements2.next().unwrap();
85+
if elements[elements.len() - 1] == tp {
86+
// Don't push duplicates
87+
continue;
88+
}
89+
elements.push(tp);
90+
}
91+
}
92+
}
93+
if elements[elements.len() - 1] == elem {
94+
// Don't push duplicates
95+
continue;
96+
}
97+
elements.push(elem);
98+
}
99+
// Finish draining second list
100+
for elem in elements2 {
101+
if elements[elements.len() - 1] == elem {
102+
continue;
103+
}
104+
// from_2 += 1;
105+
elements.push(elem);
106+
}
107+
58108
Relation { elements }
59109
}
60110

0 commit comments

Comments
 (0)