diff --git a/src/crates/benchmarker/Cargo.toml b/src/crates/benchmarker/Cargo.toml index 3fffe5e..875ed26 100644 --- a/src/crates/benchmarker/Cargo.toml +++ b/src/crates/benchmarker/Cargo.toml @@ -17,3 +17,7 @@ harness = false [[bench]] name = "linked_list" harness = false + +[[bench]] +name = "hashmap" +harness = false diff --git a/src/crates/benchmarker/benches/hashmap.rs b/src/crates/benchmarker/benches/hashmap.rs new file mode 100644 index 0000000..dbd498a --- /dev/null +++ b/src/crates/benchmarker/benches/hashmap.rs @@ -0,0 +1,7 @@ +fn main() { + let ns = [ + 64, 128, 256, 512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 16384, 24576, 32768, + 40960, 49152, 57344, 65536, + ]; + candelabra_benchmarker::benchmark_mapping::, _, _>(&ns); +} diff --git a/src/crates/benchmarker/src/lib.rs b/src/crates/benchmarker/src/lib.rs index 3018f56..f449527 100644 --- a/src/crates/benchmarker/src/lib.rs +++ b/src/crates/benchmarker/src/lib.rs @@ -1,5 +1,6 @@ mod container; mod indexable; +mod mapping; mod stack; use std::{ @@ -9,6 +10,7 @@ use std::{ pub use container::*; pub use indexable::*; +pub use mapping::*; pub use stack::*; const WARM_UP_TIME: Duration = Duration::from_millis(500); diff --git a/src/crates/benchmarker/src/mapping.rs b/src/crates/benchmarker/src/mapping.rs new file mode 100644 index 0000000..b567b30 --- /dev/null +++ b/src/crates/benchmarker/src/mapping.rs @@ -0,0 +1,141 @@ +use primrose_library::traits::Mapping; +use rand::{ + distributions::{Distribution, Standard}, + random, thread_rng, Rng, +}; + +use crate::{benchmark_op, print_result}; + +pub fn benchmark_mapping(ns: &[usize]) +where + T: Mapping + Default + Clone, + K: Copy, + V: Copy, + Standard: Distribution + Distribution, +{ + for n in ns { + scenario_populate::(*n); + scenario_contains::(*n); + scenario_remove::(*n); + scenario_clear::(*n); + } +} + +fn scenario_populate(n: usize) +where + T: Mapping + Default + Clone, + K: Copy, + V: Copy, + Standard: Distribution + Distribution, +{ + let mut results = benchmark_op( + || { + ( + T::default(), + (0..n) + .map(|_| (random(), random())) + .collect::>(), + ) + }, + |(c, xs)| { + for (k, v) in xs { + c.insert(*k, *v); + } + }, + ); + + // Since we've repeated n times in each run + results.min_nanos /= n as f64; + results.avg_nanos /= n as f64; + results.max_nanos /= n as f64; + + print_result("insert", n, results); +} + +fn scenario_contains(n: usize) +where + T: Mapping + Default + Clone, + K: Copy, + V: Copy, + Standard: Distribution + Distribution, +{ + let results = benchmark_op( + || { + let mut rng = thread_rng(); + let mut c = T::default(); + + // decide where the element that we will search for will be + let pivot = rng.gen_range(0..n); + + // insert the element at pivot, and keep track of what it is + for _ in 0..pivot { + c.insert(random(), random()); + } + let chosen = rng.gen(); + c.insert(chosen, random()); + for _ in pivot..n { + c.insert(random(), random()); + } + + (c, chosen) + }, + |(c, chosen)| c.contains(chosen), + ); + + print_result("contains", n, results); +} + +fn scenario_remove(n: usize) +where + T: Mapping + Default + Clone, + K: Copy, + V: Copy, + Standard: Distribution + Distribution, +{ + let results = benchmark_op( + || { + let mut rng = thread_rng(); + let mut c = T::default(); + + // decide where the element that we will search for will be + let pivot = rng.gen_range(0..n); + + // insert the element at pivot, and keep track of what it is + for _ in 0..pivot { + c.insert(random(), random()); + } + let chosen = rng.gen(); + c.insert(chosen, random()); + for _ in pivot..n { + c.insert(random(), random()); + } + + (c, chosen) + }, + |(c, chosen)| c.remove(chosen), + ); + + print_result("remove", n, results); +} + +fn scenario_clear(n: usize) +where + T: Mapping + Default + Clone, + K: Copy, + V: Copy, + Standard: Distribution + Distribution, +{ + let results = benchmark_op( + || { + let mut c = T::default(); + + for _ in 0..n { + c.insert(random(), random()); + } + c + }, + |c| c.clear(), + ); + + print_result("clear", n, results); +} diff --git a/src/crates/library/src/eager_sorted_vector.rs b/src/crates/library/src/eager_sorted_vector.rs index 9be45d0..bf67105 100644 --- a/src/crates/library/src/eager_sorted_vector.rs +++ b/src/crates/library/src/eager_sorted_vector.rs @@ -23,7 +23,7 @@ impl EagerSortedVec { EagerSortedVec { v: Vec::new() } } - pub fn len(&mut self) -> usize { + pub fn len(&self) -> usize { self.v.len() } @@ -31,7 +31,7 @@ impl EagerSortedVec { self.v.binary_search(x).is_ok() } - pub fn is_empty(&mut self) -> bool { + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -81,7 +81,7 @@ impl Container for EagerSortedVec { (define (pre-len xs) (equal? xs (sort xs <))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { EagerSortedVec::len(self) } @@ -108,7 +108,7 @@ impl Container for EagerSortedVec { (define (pre-is-empty xs) (equal? xs (sort xs <))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { EagerSortedVec::is_empty(self) } diff --git a/src/crates/library/src/eager_unique_vector.rs b/src/crates/library/src/eager_unique_vector.rs index b5e0284..632762b 100644 --- a/src/crates/library/src/eager_unique_vector.rs +++ b/src/crates/library/src/eager_unique_vector.rs @@ -28,7 +28,7 @@ impl EagerUniqueVec { EagerUniqueVec { v: Vec::new() } } - pub fn len(&mut self) -> usize { + pub fn len(&self) -> usize { self.v.len() } @@ -36,7 +36,7 @@ impl EagerUniqueVec { self.v.contains(x) } - pub fn is_empty(&mut self) -> bool { + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -88,7 +88,7 @@ impl Container for EagerUniqueVec { (define (pre-len xs) (equal? xs (remove-duplicates xs))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { EagerUniqueVec::len(self) } @@ -115,7 +115,7 @@ impl Container for EagerUniqueVec { (define (pre-is-empty xs) (equal? xs (remove-duplicates xs))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { EagerUniqueVec::is_empty(self) } diff --git a/src/crates/library/src/hashmap.rs b/src/crates/library/src/hashmap.rs new file mode 100644 index 0000000..ace6453 --- /dev/null +++ b/src/crates/library/src/hashmap.rs @@ -0,0 +1,41 @@ +/*LIBSPEC-NAME* +rust-hashmap-spec std::collections::HashMap +*ENDLIBSPEC-NAME*/ + +use crate::traits::Mapping; +pub use std::collections::HashMap; +use std::hash::Hash; + +/*IMPL* +Mapping +*ENDIMPL*/ +impl Mapping for HashMap { + fn len(&self) -> usize { + HashMap::len(self) + } + + fn contains(&mut self, x: &K) -> bool { + HashMap::contains_key(self, x) + } + + fn insert(&mut self, key: K, val: V) -> Option { + HashMap::insert(self, key, val) + } + + fn get(&mut self, key: &K) -> Option<&V> { + HashMap::get(self, key) + } + + fn remove(&mut self, key: &K) -> Option { + HashMap::remove(self, key) + } + + fn clear(&mut self) { + HashMap::clear(self) + } +} + +#[cfg(test)] +mod tests { + // TODO +} diff --git a/src/crates/library/src/hashset.rs b/src/crates/library/src/hashset.rs index d7d8915..f8f637c 100644 --- a/src/crates/library/src/hashset.rs +++ b/src/crates/library/src/hashset.rs @@ -18,7 +18,7 @@ impl Container for HashSet { (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { HashSet::len(self) } @@ -45,7 +45,7 @@ impl Container for HashSet { (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { HashSet::is_empty(self) } diff --git a/src/crates/library/src/lazy_sorted_vector.rs b/src/crates/library/src/lazy_sorted_vector.rs index 5ead065..17cb7f6 100644 --- a/src/crates/library/src/lazy_sorted_vector.rs +++ b/src/crates/library/src/lazy_sorted_vector.rs @@ -27,11 +27,7 @@ impl LazySortedVec { } } - pub fn len(&mut self) -> usize { - if self.modified { - self.v.sort(); - self.modified = false; - } + pub fn len(&self) -> usize { self.v.len() } @@ -43,11 +39,7 @@ impl LazySortedVec { self.v.binary_search(x).is_ok() } - pub fn is_empty(&mut self) -> bool { - if self.modified { - self.v.sort(); - self.modified = false; - } + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -117,7 +109,7 @@ impl Container for LazySortedVec { (define (pre-len xs) (equal? xs (sort xs <))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { LazySortedVec::len(self) } @@ -144,7 +136,7 @@ impl Container for LazySortedVec { (define (pre-is-empty xs) (equal? xs (sort xs <))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { LazySortedVec::is_empty(self) } diff --git a/src/crates/library/src/lazy_unique_vector.rs b/src/crates/library/src/lazy_unique_vector.rs index 9b5e60c..e9c24dc 100644 --- a/src/crates/library/src/lazy_unique_vector.rs +++ b/src/crates/library/src/lazy_unique_vector.rs @@ -28,12 +28,7 @@ impl LazyUniqueVec { LazyUniqueVec { v, modified: false } } - pub fn len(&mut self) -> usize { - if self.modified { - self.v.sort(); - self.v.dedup(); - self.modified = false; - } + pub fn len(&self) -> usize { self.v.len() } @@ -46,12 +41,7 @@ impl LazyUniqueVec { self.v.binary_search(x).is_ok() } - pub fn is_empty(&mut self) -> bool { - if self.modified { - self.v.sort(); - self.v.dedup(); - self.modified = false; - } + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -127,7 +117,7 @@ impl Container for LazyUniqueVec { (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { LazyUniqueVec::len(self) } @@ -154,7 +144,7 @@ impl Container for LazyUniqueVec { (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { LazyUniqueVec::is_empty(self) } diff --git a/src/crates/library/src/lib.rs b/src/crates/library/src/lib.rs index 1c39482..db5aac7 100644 --- a/src/crates/library/src/lib.rs +++ b/src/crates/library/src/lib.rs @@ -12,6 +12,7 @@ mod eager_unique_vector; mod lazy_sorted_vector; mod lazy_unique_vector; +mod hashmap; mod hashset; mod list; mod treeset; diff --git a/src/crates/library/src/list.rs b/src/crates/library/src/list.rs index 191b112..4ee045c 100644 --- a/src/crates/library/src/list.rs +++ b/src/crates/library/src/list.rs @@ -19,7 +19,7 @@ impl Container for LinkedList { (define (pre-len xs) #t) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { LinkedList::len(self) } @@ -46,7 +46,7 @@ impl Container for LinkedList { (define (pre-is-empty xs) #t) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { LinkedList::is_empty(self) } diff --git a/src/crates/library/src/profiler.rs b/src/crates/library/src/profiler.rs index 952d0a0..ba8c357 100644 --- a/src/crates/library/src/profiler.rs +++ b/src/crates/library/src/profiler.rs @@ -2,12 +2,13 @@ use std::{ env::var, fs::{create_dir, metadata, File}, io::Write, + marker::PhantomData, time::SystemTime, }; -use crate::traits::{Container, Indexable, Stack}; +use crate::traits::{Container, Indexable, Mapping, Stack}; -pub struct ProfilerWrapper { +pub struct ProfilerWrapper { inner: T, sum_ns: usize, n_contains: usize, @@ -19,9 +20,11 @@ pub struct ProfilerWrapper { n_nth: usize, n_push: usize, n_pop: usize, + n_get: usize, + _d: PhantomData, } -impl Default for ProfilerWrapper { +impl Default for ProfilerWrapper { fn default() -> Self { Self { inner: T::default(), @@ -35,79 +38,131 @@ impl Default for ProfilerWrapper { n_nth: 0, n_push: 0, n_pop: 0, + n_get: 0, + _d: Default::default(), } } } -impl, E> Container for ProfilerWrapper { - fn len(&mut self) -> usize { +impl, E> ProfilerWrapper { + fn add_n(&mut self) { + self.sum_ns += self.inner.len(); + } +} + +impl, K, V> ProfilerWrapper { + fn add_n_map(&mut self) { + self.sum_ns += self.inner.len(); + } +} + +impl, E> Container for ProfilerWrapper { + fn len(&self) -> usize { self.inner.len() } fn contains(&mut self, x: &E) -> bool { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_contains += 1; self.inner.contains(x) } - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { self.inner.is_empty() } fn insert(&mut self, elt: E) { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_insert += 1; self.inner.insert(elt) } fn clear(&mut self) { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_clear += 1; self.inner.clear() } fn remove(&mut self, elt: E) -> Option { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_remove += 1; self.inner.remove(elt) } } -impl + Container, E> Indexable for ProfilerWrapper { +impl + Container, E> Indexable + for ProfilerWrapper +{ fn first(&mut self) -> Option<&E> { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_first += 1; self.inner.first() } fn last(&mut self) -> Option<&E> { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_last += 1; self.inner.last() } fn nth(&mut self, n: usize) -> Option<&E> { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_nth += 1; self.inner.nth(n) } } -impl + Container, E> Stack for ProfilerWrapper { +impl + Container, E> Stack for ProfilerWrapper { fn push(&mut self, elt: E) { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_push += 1; self.inner.push(elt) } fn pop(&mut self) -> Option { - self.sum_ns += self.inner.len(); + self.add_n(); self.n_pop += 1; self.inner.pop() } } -impl Drop for ProfilerWrapper { +impl, K, V> Mapping for ProfilerWrapper { + fn len(&self) -> usize { + self.inner.len() + } + + fn contains(&mut self, x: &K) -> bool { + self.add_n_map(); + self.n_contains += 1; + self.inner.contains(x) + } + + fn insert(&mut self, key: K, val: V) -> Option { + self.add_n_map(); + self.n_insert += 1; + self.inner.insert(key, val) + } + + fn get(&mut self, key: &K) -> Option<&V> { + self.add_n_map(); + self.n_get += 1; + self.inner.get(key) + } + + fn remove(&mut self, key: &K) -> Option { + self.add_n_map(); + self.n_remove += 1; + self.inner.remove(key) + } + + fn clear(&mut self) { + self.add_n_map(); + self.n_clear += 1; + self.inner.clear() + } +} + +impl Drop for ProfilerWrapper { fn drop(&mut self) { let base_dir = var("PROFILER_OUT_DIR").expect("ProfilerWrapper used without environment variable"); @@ -130,5 +185,6 @@ impl Drop for ProfilerWrapper { writeln!(f, "{}", self.n_nth).unwrap(); writeln!(f, "{}", self.n_push).unwrap(); writeln!(f, "{}", self.n_pop).unwrap(); + writeln!(f, "{}", self.n_get).unwrap(); } } diff --git a/src/crates/library/src/traits.rs b/src/crates/library/src/traits.rs index a29f7e0..bf4616a 100644 --- a/src/crates/library/src/traits.rs +++ b/src/crates/library/src/traits.rs @@ -1,10 +1,10 @@ //! Common traits for primrose container types pub trait Container: Default { /// Get the current number of elements - fn len(&mut self) -> usize; + fn len(&self) -> usize; /// Check if the container is empty - fn is_empty(&mut self) -> bool; + fn is_empty(&self) -> bool; /// Check if the specified item is in the container fn contains(&mut self, x: &T) -> bool; @@ -29,3 +29,28 @@ pub trait Indexable { fn last(&mut self) -> Option<&T>; fn nth(&mut self, n: usize) -> Option<&T>; } + +pub trait Mapping: Default { + /// Get the current number of elements + fn len(&self) -> usize; + + /// Check if the container is empty + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Check if the specified item is in the container + fn contains(&mut self, x: &K) -> bool; + + /// Insert the given item + fn insert(&mut self, key: K, val: V) -> Option; + + /// Get the value for the given key + fn get(&mut self, key: &K) -> Option<&V>; + + /// Remove the first occurence of the given item + fn remove(&mut self, key: &K) -> Option; + + /// Remove all elements from the container + fn clear(&mut self); +} diff --git a/src/crates/library/src/treeset.rs b/src/crates/library/src/treeset.rs index 4e2b213..f4f6923 100644 --- a/src/crates/library/src/treeset.rs +++ b/src/crates/library/src/treeset.rs @@ -17,7 +17,7 @@ impl Container for BTreeSet { (define (pre-len xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { BTreeSet::len(self) } @@ -44,7 +44,7 @@ impl Container for BTreeSet { (define (pre-is-empty xs) (equal? xs (remove-duplicates (sort xs <)))) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { BTreeSet::is_empty(self) } diff --git a/src/crates/library/src/vector.rs b/src/crates/library/src/vector.rs index 38293c8..496f4f0 100644 --- a/src/crates/library/src/vector.rs +++ b/src/crates/library/src/vector.rs @@ -17,7 +17,7 @@ impl Container for Vec { (define (pre-len xs) #t) (define (post-len xs r) (equal? r (op-len xs))) *ENDLIBSPEC*/ - fn len(&mut self) -> usize { + fn len(&self) -> usize { Vec::len(self) } @@ -44,7 +44,7 @@ impl Container for Vec { (define (pre-is-empty xs) #t) (define (post-is-empty xs r) (equal? r (op-is-empty xs))) *ENDLIBSPEC*/ - fn is_empty(&mut self) -> bool { + fn is_empty(&self) -> bool { Vec::is_empty(self) }