diff --git a/src/libcore/core.rc b/src/libcore/core.rc index f02af9c0998..4b1ace5651b 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -55,6 +55,7 @@ export hash; export cmp; export num; export path; +export managed; // NDM seems to be necessary for resolve to work export option_iter; @@ -261,6 +262,7 @@ mod sys; #[warn(non_camel_case_types)] mod unsafe; +mod managed; // Modules supporting compiler-generated code // Exported but not part of the public interface diff --git a/src/libcore/managed.rs b/src/libcore/managed.rs new file mode 100644 index 00000000000..06fcd87282b --- /dev/null +++ b/src/libcore/managed.rs @@ -0,0 +1,62 @@ +/*! + +Module for wrapping freezable data structures in managed boxes. +Normally freezable data structures require an unaliased reference, +such as `T` or `~T`, so that the compiler can track when they are +being mutated. The `rw` type converts these static checks into +dynamic checks: your program will fail if you attempt to perform +mutation when the data structure should be immutable. + +*/ + +#[forbid(non_camel_case_types)]; +#[forbid(deprecated_mode)]; +#[forbid(deprecated_pattern)]; + +import util::with; +import unsafe::transmute_immut; + +export Managed; + +enum Mode { ReadOnly, Mutable, Immutable } + +struct Data { + mut value: T; + mut mode: Mode; +} + +type Managed = @Data; + +fn Managed(+t: T) -> Managed { + @Data {value: t, mode: ReadOnly} +} + +impl Data { + fn borrow_mut(op: &fn(t: &mut T) -> R) -> R { + match self.mode { + Immutable => fail fmt!("%? currently immutable", + self.value), + ReadOnly | Mutable => {} + } + + do with(&mut self.mode, Mutable) { + op(&mut self.value) + } + } + + fn borrow_const(op: &fn(t: &const T) -> R) -> R { + op(&const self.value) + } + + fn borrow_imm(op: &fn(t: &T) -> R) -> R { + match self.mode { + Mutable => fail fmt!("%? currently mutable", + self.value), + ReadOnly | Immutable => {} + } + + do with(&mut self.mode, Immutable) { + op(unsafe{transmute_immut(&mut self.value)}) + } + } +} diff --git a/src/libcore/send_map.rs b/src/libcore/send_map.rs index 52a63dacbfc..9b5b6e0d4f3 100644 --- a/src/libcore/send_map.rs +++ b/src/libcore/send_map.rs @@ -221,6 +221,13 @@ mod linear { self.size -= 1; return true; } + + fn clear() { + for uint::range(0, self.buckets.len()) |idx| { + self.buckets[idx] = none; + } + self.size = 0; + } } priv impl &LinearMap { diff --git a/src/libcore/util.rs b/src/libcore/util.rs index c68ee317da4..8487e4ff930 100644 --- a/src/libcore/util.rs +++ b/src/libcore/util.rs @@ -12,6 +12,24 @@ pure fn id(+x: T) -> T { x } /// Ignores a value. pure fn ignore(+_x: T) { } +/// Sets `*ptr` to `new_value`, invokes `op()`, and then restores the +/// original value of `*ptr`. +#[inline(always)] +fn with( + ptr: &mut T, + +new_value: T, + op: &fn() -> R) -> R +{ + // NDM: if swap operator were defined somewhat differently, + // we wouldn't need to copy... + + let old_value = *ptr; + *ptr = move new_value; + let result = op(); + *ptr = move old_value; + return move result; +} + /** * Swap the values at two mutable locations of the same type, without * deinitialising or copying either one. diff --git a/src/libstd/map.rs b/src/libstd/map.rs index e6f44d76b4c..538a6446001 100644 --- a/src/libstd/map.rs +++ b/src/libstd/map.rs @@ -4,6 +4,9 @@ import io::WriterUtil; import to_str::ToStr; +import managed::Managed; +import send_map::linear::LinearMap; + export hashmap, hashfn, eqfn, set, map, chained, hashmap, str_hash; export box_str_hash; export bytes_hash, int_hash, uint_hash, set_add; @@ -59,10 +62,10 @@ trait map { fn find(+key: K) -> option; /** - * Remove and return a value from the map. If the key does not exist - * in the map then returns none. + * Remove and return a value from the map. Returns true if the + * key was present in the map, otherwise false. */ - fn remove(+key: K) -> option; + fn remove(+key: K) -> bool; /// Clear the map, removing all key/value pairs. fn clear(); @@ -279,18 +282,18 @@ mod chained { option::unwrap(opt_v) } - fn remove(+k: K) -> option { + fn remove(+k: K) -> bool { match self.search_tbl(&k, self.hasher(&k)) { - not_found => none, + not_found => false, found_first(idx, entry) => { self.count -= 1u; self.chains[idx] = entry.next; - some(entry.value) + true } found_after(eprev, entry) => { self.count -= 1u; eprev.next = entry.next; - some(entry.value) + true } } } @@ -468,6 +471,93 @@ fn hash_from_uints(items: &[(uint, V)]) -> hashmap { hash_from_vec(uint::hash, uint::eq, items) } +// XXX Transitionary +impl Managed>: map { + fn size() -> uint { + do self.borrow_const |p| { + p.len() + } + } + + fn insert(+key: K, +value: V) -> bool { + do self.borrow_mut |p| { + p.insert(key, value) + } + } + + fn contains_key(+key: K) -> bool { + do self.borrow_const |p| { + p.contains_key(&key) + } + } + + fn contains_key_ref(key: &K) -> bool { + do self.borrow_const |p| { + p.contains_key(key) + } + } + + fn get(+key: K) -> V { + do self.borrow_const |p| { + p.get(&key) + } + } + + fn find(+key: K) -> option { + do self.borrow_const |p| { + p.find(&key) + } + } + + fn remove(+key: K) -> bool { + do self.borrow_mut |p| { + p.remove(&key) + } + } + + fn clear() { + do self.borrow_mut |p| { + p.clear() + } + } + + fn each(op: fn(+key: K, +value: V) -> bool) { + do self.borrow_imm |p| { + p.each(op) + } + } + + fn each_key(op: fn(+key: K) -> bool) { + do self.borrow_imm |p| { + p.each_key(op) + } + } + + fn each_value(op: fn(+value: V) -> bool) { + do self.borrow_imm |p| { + p.each_value(op) + } + } + + fn each_ref(op: fn(key: &K, value: &V) -> bool) { + do self.borrow_imm |p| { + p.each_ref(op) + } + } + + fn each_key_ref(op: fn(key: &K) -> bool) { + do self.borrow_imm |p| { + p.each_key_ref(op) + } + } + + fn each_value_ref(op: fn(value: &V) -> bool) { + do self.borrow_imm |p| { + p.each_value_ref(op) + } + } +} + #[cfg(test)] mod tests { diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index a7b40bbb0f9..04b04f505a4 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -80,13 +80,13 @@ impl smallintmap: map::map { insert(self, key, value); return !exists; } - fn remove(+key: uint) -> option { + fn remove(+key: uint) -> bool { if key >= self.v.len() { - return none; + return false; } let old = self.v.get_elt(key); self.v.set_elt(key, none); - old + old.is_some() } fn clear() { self.v.set(~[mut]); diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs new file mode 100644 index 00000000000..9a267a5a25d --- /dev/null +++ b/src/test/bench/core-map.rs @@ -0,0 +1,178 @@ +/* + + + +*/ + +use std; +import rand; +import std::map; +import managed::Managed; +import send_map::linear::*; +import io::WriterUtil; + +struct Results { + sequential_ints: float; + random_ints: float; + delete_ints: float; + + sequential_strings: float; + random_strings: float; + delete_strings: float; +} + +fn timed(result: &mut float, + op: fn()) { + let start = std::time::precise_time_s(); + op(); + let end = std::time::precise_time_s(); + *result = (end - start); +} + +fn int_benchmarks>(make_map: fn() -> M, + rng: @rand::Rng, + num_keys: uint, + results: &mut Results) { + + { + let map = make_map(); + do timed(&mut results.sequential_ints) { + for uint::range(0, num_keys) |i| { + map.insert(i, i+1); + } + + for uint::range(0, num_keys) |i| { + assert map.get(i) == i+1; + } + } + } + + { + let map = make_map(); + do timed(&mut results.random_ints) { + for uint::range(0, num_keys) |i| { + map.insert(rng.next() as uint, i); + } + } + } + + { + let map = make_map(); + for uint::range(0, num_keys) |i| { + map.insert(i, i);; + } + + do timed(&mut results.delete_ints) { + for uint::range(0, num_keys) |i| { + assert map.remove(i); + } + } + } +} + +fn str_benchmarks>(make_map: fn() -> M, + rng: @rand::Rng, + num_keys: uint, + results: &mut Results) { + { + let map = make_map(); + do timed(&mut results.sequential_strings) { + for uint::range(0, num_keys) |i| { + let s = uint::to_str(i, 10); + map.insert(s, i); + } + + for uint::range(0, num_keys) |i| { + let s = uint::to_str(i, 10); + assert map.get(s) == i; + } + } + } + + { + let map = make_map(); + do timed(&mut results.random_strings) { + for uint::range(0, num_keys) |i| { + let s = uint::to_str(rng.next() as uint, 10); + map.insert(s, i); + } + } + } + + { + let map = make_map(); + for uint::range(0, num_keys) |i| { + map.insert(uint::to_str(i, 10), i); + } + do timed(&mut results.delete_strings) { + for uint::range(0, num_keys) |i| { + assert map.remove(uint::to_str(i, 10)); + } + } + } +} + +fn write_header(header: &str) { + io::stdout().write_str(header); + io::stdout().write_str("\n"); +} + +fn write_row(label: &str, value: float) { + io::stdout().write_str(fmt!("%30s %f s\n", label, value)); +} + +fn write_results(label: &str, results: &Results) { + write_header(label); + write_row("sequential_ints", results.sequential_ints); + write_row("random_ints", results.random_ints); + write_row("delete_ints", results.delete_ints); + write_row("sequential_strings", results.sequential_strings); + write_row("random_strings", results.random_strings); + write_row("delete_strings", results.delete_strings); +} + +fn empty_results() -> Results { + Results { + sequential_ints: 0f, + random_ints: 0f, + delete_ints: 0f, + + sequential_strings: 0f, + random_strings: 0f, + delete_strings: 0f, + } +} + +fn main(args: ~[~str]) { + let num_keys = { + if args.len() == 2 { + uint::from_str(args[1]).get() + } else { + 100 // woefully inadequate for any real measurement + } + }; + + let seed = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + { + let rng = rand::seeded_rng(copy seed); + let mut results = empty_results(); + int_benchmarks::>( + map::uint_hash, rng, num_keys, &mut results); + str_benchmarks::>( + map::str_hash, rng, num_keys, &mut results); + write_results("libstd::map::hashmap", &results); + } + + { + let rng = rand::seeded_rng(copy seed); + let mut results = empty_results(); + int_benchmarks::>>( + || Managed(linear_map(uint::hash, uint::eq)), + rng, num_keys, &mut results); + str_benchmarks::>>( + || Managed(linear_map(str::hash, str::eq)), + rng, num_keys, &mut results); + write_results("libstd::map::hashmap", &results); + } +} \ No newline at end of file