diff options
| author | bors <bors@rust-lang.org> | 2014-01-14 18:56:36 -0800 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-01-14 18:56:36 -0800 |
| commit | e6d9214ee19155ca334e0fd3d14ff82852c7c644 (patch) | |
| tree | 684cdeb55f5350e4d2dec257800cbd6d80a956d8 /src/libstd | |
| parent | dd8b011319f5cfbfb3329d9dad185be884f3a4d6 (diff) | |
| parent | e1ebdb879053f1267245110cad9b33849b3d74f3 (diff) | |
| download | rust-e6d9214ee19155ca334e0fd3d14ff82852c7c644.tar.gz rust-e6d9214ee19155ca334e0fd3d14ff82852c7c644.zip | |
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.
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/trie.rs | 118 |
1 files changed, 84 insertions, 34 deletions
diff --git a/src/libstd/trie.rs b/src/libstd/trie.rs index d864cde2953..d8df84bbba8 100644 --- a/src/libstd/trie.rs +++ b/src/libstd/trie.rs @@ -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); - - *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; + // 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)); + } + _ => {} + } + + // 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 @@ mod bench_map { } }); } + + #[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)] |
