Skip to content

Commit 5adb025

Browse files
committed
FEAT: Faster, in place-er, .sort_by for OrderMap (and OrderSet)
- See implementation comments
1 parent 6dec9e4 commit 5adb025

File tree

1 file changed

+24
-29
lines changed

1 file changed

+24
-29
lines changed

src/lib.rs

+24-29
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ fn hash_elem_using<B: BuildHasher, K: ?Sized + Hash>(build: &B, k: &K) -> HashVa
4545
#[derive(Copy, Debug)]
4646
struct HashValue(usize);
4747

48+
impl HashValue {
49+
#[inline(always)]
50+
fn get(self) -> usize { self.0 }
51+
}
52+
4853
impl Clone for HashValue {
4954
#[inline]
5055
fn clone(&self) -> Self { *self }
@@ -1059,37 +1064,27 @@ impl<K, V, S> OrderMap<K, V, S>
10591064
pub fn sort_by<F>(&mut self, mut compare: F)
10601065
where F: FnMut(&K, &V, &K, &V) -> Ordering,
10611066
{
1062-
// new_index will form a lookup map from current index -> new index.
1063-
let mut new_index = Vec::from_iter(0..self.len());
1064-
new_index.sort_by(|&i, &j| {
1065-
let ei = &self.entries[i];
1066-
let ej = &self.entries[j];
1067-
compare(&ei.key, &ei.value, &ej.key, &ej.value)
1068-
});
1067+
// a bucket is Key-Value-Hash; here we temporarily use the hash field to
1068+
// store the old index instead.
1069+
//
1070+
// Save the old hash values in `side_index`.
1071+
//
1072+
// Then we can sort `self.entries` in place.
1073+
let mut side_index = Vec::from_iter(enumerate(&mut self.entries).map(|(i, elt)| {
1074+
replace(&mut elt.hash, HashValue(i)).get()
1075+
}));
10691076

1070-
// Apply new index to self.indices
1071-
dispatch_32_vs_64!(self.apply_new_index(&new_index));
1072-
1073-
// Apply new index to entries
1074-
apply_permutation(&mut new_index, &mut self.entries);
1075-
1076-
/// Apply a permutation
1077-
///
1078-
/// perm: Each index 0..v.len() appear exactly once.
1079-
fn apply_permutation<T>(perm: &mut [usize], v: &mut [T]) {
1080-
debug_assert_eq!(perm.len(), v.len());
1081-
1082-
for i in 0..perm.len() {
1083-
let mut current = i;
1084-
while i != perm[current] {
1085-
let next = replace(&mut perm[current], current);
1086-
// move element from next to current
1087-
v.swap(next, current);
1088-
current = next;
1089-
}
1090-
perm[current] = current;
1091-
}
1077+
self.entries.sort_by(move |ei, ej| compare(&ei.key, &ei.value, &ej.key, &ej.value));
1078+
1079+
// Here we write back the hash values from side_index and fill
1080+
// in side_index with a mapping from old the new index instead.
1081+
for (i, ent) in enumerate(&mut self.entries) {
1082+
let old_index = ent.hash.get();
1083+
ent.hash = HashValue(replace(&mut side_index[old_index], i));
10921084
}
1085+
1086+
// Apply new index to self.indices
1087+
dispatch_32_vs_64!(self.apply_new_index(&side_index));
10931088
}
10941089

10951090
fn apply_new_index<Sz>(&mut self, new_index: &[usize])

0 commit comments

Comments
 (0)