Skip to content

Commit 2ebb5c6

Browse files
authored
perf: use wrapping operations, use next_power_of_two, improve tests (#10)
* Use `next_power_of_two` which should be more performant than manual optimization * Clean up tests so they don't hash more than necessary * Use wrapping operations in case hash operations overflow
1 parent 078289b commit 2ebb5c6

File tree

2 files changed

+13
-25
lines changed

2 files changed

+13
-25
lines changed

src/iterator.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,11 @@ mod test {
9797
// Check that each entry doesn't exist
9898
// Check that every number is "hit" (as they'd have to be) for a perfect bijection
9999
// Check that the number is within range
100-
let mut set = HashSet::new();
100+
let mut set = HashSet::with_capacity(length.get() as usize);
101101

102102
for elem in it {
103-
let set_result = set.get(&elem);
104-
105103
// Make sure there are no duplicates
106-
assert!(set_result.is_none());
107-
set.insert(elem);
104+
assert!(set.insert(elem));
108105
}
109106
// Need to dereference the types into regular integers
110107
let mut result: Vec<u32> = set.into_iter().collect();

src/kensler.rs

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::error::{PermutationError, PermutationResult};
88
#[cfg(feature = "use-rand")]
99
use rand::prelude::*;
10-
use std::num::NonZeroU32;
10+
use std::num::{NonZeroU32, Wrapping};
1111

1212
/// The `HashedPermutation` struct stores the initial `seed` and `length` of the permutation
1313
/// vector. In other words, if you want to shuffle the numbers from `0..n`, then `length = n`.
@@ -56,17 +56,12 @@ impl HashedPermutation {
5656
max_shuffle: self.length.get(),
5757
});
5858
}
59-
let mut i = input;
59+
let mut i = Wrapping(input);
6060
let n = self.length.get();
61-
let seed = self.seed;
62-
let mut w = n - 1;
63-
w |= w >> 1;
64-
w |= w >> 2;
65-
w |= w >> 4;
66-
w |= w >> 8;
67-
w |= w >> 16;
68-
69-
while i >= n {
61+
let seed = Wrapping(self.seed);
62+
let w = Wrapping(n.checked_next_power_of_two().map_or(u32::MAX, |x| x - 1));
63+
64+
while i.0 >= n {
7065
i ^= seed;
7166
i *= 0xe170893d;
7267
i ^= seed >> 16;
@@ -75,7 +70,7 @@ impl HashedPermutation {
7570
i *= 0x0929eb3f;
7671
i ^= seed >> 23;
7772
i ^= (i & w) >> 1;
78-
i *= 1 | seed >> 27;
73+
i *= Wrapping(1) | seed >> 27;
7974
i *= 0x6935fa69;
8075
i ^= (i & w) >> 11;
8176
i *= 0x74dcb303;
@@ -86,7 +81,7 @@ impl HashedPermutation {
8681
i &= w;
8782
i ^= i >> 5;
8883
}
89-
Ok((i + seed) % n)
84+
Ok((i + seed).0 % n)
9085
}
9186
}
9287

@@ -143,19 +138,15 @@ mod test {
143138
// Check that each entry doesn't exist
144139
// Check that every number is "hit" (as they'd have to be) for a perfect bijection
145140
// Check that the number is within range
146-
let mut map = HashMap::new();
141+
let mut map = HashMap::with_capacity(length.get() as usize);
147142

148143
for i in 0..perm.length.get() {
149144
let res = perm.shuffle(i);
150145
let res = res.unwrap();
151-
let map_result = map.get(&res);
152-
assert!(map_result.is_none());
153-
map.insert(res, i);
146+
assert!(map.insert(res, i).is_none());
154147
}
155-
// Need to dereference the types into regular integers
156-
let mut keys_vec: Vec<u32> = map.keys().into_iter().map(|k| *k).collect();
148+
let (mut keys_vec, mut vals_vec): (Vec<u32>, Vec<u32>) = map.iter().unzip();
157149
keys_vec.sort();
158-
let mut vals_vec: Vec<u32> = map.values().into_iter().map(|v| *v).collect();
159150
vals_vec.sort();
160151
let ground_truth: Vec<u32> = (0..length.get()).collect();
161152
assert_eq!(ground_truth, keys_vec);

0 commit comments

Comments
 (0)