auto merge of #11546 : huonw/rust/trie-insert, r=alexcrichton
This reduces the number of moves/memcpy's we do, which makes insert faster, especially in cases of keys with long equal prefixes (the \_low_bits tests): Before: bench_insert_large ... bench: 553966 ns/iter (+/- 64050) bench_insert_large_low_bits ... bench: 1048151 ns/iter (+/- 92484) bench_insert_small ... bench: 168840 ns/iter (+/- 22410) bench_insert_small_low_bits ... bench: 185069 ns/iter (+/- 38332) After: bench_insert_large ... bench: 422132 ns/iter (+/- 35112) bench_insert_large_low_bits ... bench: 339083 ns/iter (+/- 34421) bench_insert_small ... bench: 134539 ns/iter (+/- 15254) bench_insert_small_low_bits ... bench: 88775 ns/iter (+/- 5746) Notably: no unsafe code.
This commit is contained in:
commit
e6d9214ee1
@ -12,7 +12,7 @@
|
||||
|
||||
use prelude::*;
|
||||
use uint;
|
||||
use util::{swap, replace};
|
||||
use util::replace;
|
||||
use vec;
|
||||
|
||||
// FIXME: #5244: need to manually update the TrieNode constructor
|
||||
@ -415,39 +415,41 @@ fn find_mut<'r, T>(child: &'r mut Child<T>, key: uint, idx: uint) -> Option<&'r
|
||||
|
||||
fn insert<T>(count: &mut uint, child: &mut Child<T>, key: uint, value: T,
|
||||
idx: uint) -> Option<T> {
|
||||
let mut tmp = Nothing;
|
||||
let ret;
|
||||
swap(&mut tmp, child);
|
||||
// we branch twice to avoid having to do the `replace` when we
|
||||
// don't need to; this is much faster, especially for keys that
|
||||
// have long shared prefixes.
|
||||
match *child {
|
||||
Nothing => {
|
||||
*count += 1;
|
||||
*child = External(key, value);
|
||||
return None;
|
||||
}
|
||||
Internal(ref mut x) => {
|
||||
return insert(&mut x.count, &mut x.children[chunk(key, idx)], key, value, idx + 1);
|
||||
}
|
||||
External(stored_key, ref mut stored_value) if stored_key == key => {
|
||||
// swap in the new value and return the old.
|
||||
return Some(replace(stored_value, value));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
*child = match tmp {
|
||||
External(stored_key, stored_value) => {
|
||||
if stored_key == key {
|
||||
ret = Some(stored_value);
|
||||
External(stored_key, value)
|
||||
} else {
|
||||
// conflict - split the node
|
||||
let mut new = ~TrieNode::new();
|
||||
insert(&mut new.count,
|
||||
&mut new.children[chunk(stored_key, idx)],
|
||||
stored_key, stored_value, idx + 1);
|
||||
ret = insert(&mut new.count, &mut new.children[chunk(key, idx)],
|
||||
key, value, idx + 1);
|
||||
Internal(new)
|
||||
}
|
||||
}
|
||||
Internal(x) => {
|
||||
let mut x = x;
|
||||
ret = insert(&mut x.count, &mut x.children[chunk(key, idx)], key,
|
||||
value, idx + 1);
|
||||
Internal(x)
|
||||
}
|
||||
Nothing => {
|
||||
*count += 1;
|
||||
ret = None;
|
||||
External(key, value)
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
// conflict, an external node with differing keys: we have to
|
||||
// split the node, so we need the old value by value; hence we
|
||||
// have to move out of `child`.
|
||||
match replace(child, Nothing) {
|
||||
External(stored_key, stored_value) => {
|
||||
let mut new = ~TrieNode::new();
|
||||
insert(&mut new.count,
|
||||
&mut new.children[chunk(stored_key, idx)],
|
||||
stored_key, stored_value, idx + 1);
|
||||
let ret = insert(&mut new.count, &mut new.children[chunk(key, idx)],
|
||||
key, value, idx + 1);
|
||||
*child = Internal(new);
|
||||
return ret;
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn remove<T>(count: &mut uint, child: &mut Child<T>, key: uint,
|
||||
@ -886,6 +888,54 @@ fn bench_upper_bound(bh: &mut BenchHarness) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_large(bh: &mut BenchHarness) {
|
||||
let mut m = TrieMap::<[uint, .. 10]>::new();
|
||||
let mut rng = weak_rng();
|
||||
|
||||
bh.iter(|| {
|
||||
for _ in range(0, 1000) {
|
||||
m.insert(rng.gen(), [1, .. 10]);
|
||||
}
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn bench_insert_large_low_bits(bh: &mut BenchHarness) {
|
||||
let mut m = TrieMap::<[uint, .. 10]>::new();
|
||||
let mut rng = weak_rng();
|
||||
|
||||
bh.iter(|| {
|
||||
for _ in range(0, 1000) {
|
||||
// only have the last few bits set.
|
||||
m.insert(rng.gen::<uint>() & 0xff_ff, [1, .. 10]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_insert_small(bh: &mut BenchHarness) {
|
||||
let mut m = TrieMap::<()>::new();
|
||||
let mut rng = weak_rng();
|
||||
|
||||
bh.iter(|| {
|
||||
for _ in range(0, 1000) {
|
||||
m.insert(rng.gen(), ());
|
||||
}
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn bench_insert_small_low_bits(bh: &mut BenchHarness) {
|
||||
let mut m = TrieMap::<()>::new();
|
||||
let mut rng = weak_rng();
|
||||
|
||||
bh.iter(|| {
|
||||
for _ in range(0, 1000) {
|
||||
// only have the last few bits set.
|
||||
m.insert(rng.gen::<uint>() & 0xff_ff, ());
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user