From 33f1bc6ace685bc9637f5a3855bcb66e81b75bba Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Tue, 14 Mar 2017 11:17:19 -0700 Subject: [PATCH 1/4] Add HeapSizeOf trait and from_slice method --- Cargo.toml | 3 +++ lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0a7b176..84b2fce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ documentation = "http://doc.servo.org/smallvec/" [lib] name = "smallvec" path = "lib.rs" + +[dependencies] +heapsize = "0.3" diff --git a/lib.rs b/lib.rs index 762b838..ebb7e22 100644 --- a/lib.rs +++ b/lib.rs @@ -6,16 +6,20 @@ //! to the heap for larger allocations. This can be a useful optimization for improving cache //! locality and reducing allocator traffic for workloads that fit within the inline buffer. +extern crate heapsize; + use std::borrow::{Borrow, BorrowMut}; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator}; use std::mem; +use std::os::raw::c_void; use std::ops; use std::ptr; use std::slice; +use heapsize::{HeapSizeOf, heap_size_of}; use SmallVecData::{Inline, Heap}; /// Common operations implemented by both `Vec` and `SmallVec`. @@ -478,6 +482,12 @@ impl SmallVec { } impl SmallVec where A::Item: Copy { + pub fn from_slice(slice: &[A::Item]) -> Self { + let mut vec = Self::new(); + vec.extend_from_slice(slice); + vec + } + pub fn insert_from_slice(&mut self, index: usize, slice: &[A::Item]) { self.reserve(slice.len()); @@ -500,6 +510,19 @@ impl SmallVec where A::Item: Copy { } } +impl HeapSizeOf for SmallVec where A::Item: HeapSizeOf { + fn heap_size_of_children(&self) -> usize { + match self.data { + Inline { .. } => 0, + Heap { ptr, .. } => { + self.iter().fold( + unsafe { heap_size_of(ptr as *const c_void) }, + |n, elem| n + elem.heap_size_of_children()) + }, + } + } +} + impl ops::Deref for SmallVec { type Target = [A::Item]; #[inline] @@ -823,15 +846,17 @@ macro_rules! impl_array( } ); -impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, +impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000); #[cfg(test)] pub mod tests { use SmallVec; + use heapsize::HeapSizeOf; use std::borrow::ToOwned; use std::iter::FromIterator; + use std::mem::size_of; // We heap allocate all these strings so that double frees will show up under valgrind. @@ -1248,6 +1273,35 @@ pub mod tests { assert_eq!(&SmallVec::<[u32; 2]>::from(&[1, 2, 3][..])[..], [1, 2, 3]); } + #[test] + fn test_from_slice() { + assert_eq!(&SmallVec::<[u32; 2]>::from_slice(&[1][..])[..], [1]); + assert_eq!(&SmallVec::<[u32; 2]>::from_slice(&[1, 2, 3][..])[..], [1, 2, 3]); + } + + #[test] + fn test_heap_size_of_children() { + let mut vec = SmallVec::<[u32; 2]>::new(); + assert_eq!(vec.heap_size_of_children(), 0); + vec.push(1); + vec.push(2); + assert_eq!(vec.heap_size_of_children(), 0); + vec.push(3); + assert_eq!(vec.heap_size_of_children(), 16); + + // Now check with reserved space + let mut vec = SmallVec::<[u32; 2]>::new(); + vec.reserve(10); // Rounds up to 16 + assert_eq!(vec.heap_size_of_children(), 64); + + // Check with nested heap structures + let mut vec = SmallVec::<[Vec; 2]>::new(); + vec.reserve(10); + vec.push(vec![2, 3, 4]); + assert_eq!(vec.heap_size_of_children(), + vec![2, 3, 4].heap_size_of_children() + 16 * size_of::>()); + } + #[test] fn test_exact_size_iterator() { let mut vec = SmallVec::<[u32; 2]>::from(&[1, 2, 3][..]); From 82ee158e684a1b0ca007a38ceb40c0b61f5ad25e Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Tue, 14 Mar 2017 12:10:18 -0700 Subject: [PATCH 2/4] Add a benchmark for from_slice Because it seems like good practice. --- benches/bench.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/benches/bench.rs b/benches/bench.rs index 661b0d9..3ca112a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -63,6 +63,15 @@ fn bench_extend(b: &mut Bencher) { }); } +#[bench] +fn bench_from_slice(b: &mut Bencher) { + let v: Vec = (0..100).collect(); + b.iter(|| { + let vec: SmallVec<[u64; 16]> = SmallVec::from_slice(&v); + vec + }); +} + #[bench] fn bench_extend_from_slice(b: &mut Bencher) { let v: Vec = (0..100).collect(); From cd5250d145c739d3e6de342efcf07d7d96fbe6ef Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Thu, 16 Mar 2017 11:14:10 -0700 Subject: [PATCH 3/4] Add featureflag for heapsizeof --- .travis.yml | 2 ++ Cargo.toml | 5 ++++- lib.rs | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0884694..8a946c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,9 @@ rust: - stable script: | cargo build --verbose && + cargo build --features=heapsizeof --verbose && cargo test --verbose && + cargo test --features=heapsizeof --verbose && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) notifications: webhooks: http://build.servo.org:54856/travis diff --git a/Cargo.toml b/Cargo.toml index 84b2fce..fce5deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,12 @@ keywords = ["small", "vec", "vector", "stack"] readme = "README.md" documentation = "http://doc.servo.org/smallvec/" +[features] +heapsizeof = ["heapsize"] + [lib] name = "smallvec" path = "lib.rs" [dependencies] -heapsize = "0.3" +heapsize = { version = "0.3", optional = true } diff --git a/lib.rs b/lib.rs index ebb7e22..fe1f63f 100644 --- a/lib.rs +++ b/lib.rs @@ -6,6 +6,9 @@ //! to the heap for larger allocations. This can be a useful optimization for improving cache //! locality and reducing allocator traffic for workloads that fit within the inline buffer. +#![feature(specialization)] + +#[cfg(feature="heapsizeof")] extern crate heapsize; use std::borrow::{Borrow, BorrowMut}; @@ -14,11 +17,13 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator}; use std::mem; -use std::os::raw::c_void; use std::ops; use std::ptr; use std::slice; +#[cfg(feature="heapsizeof")] +use std::os::raw::c_void; +#[cfg(feature="heapsizeof")] use heapsize::{HeapSizeOf, heap_size_of}; use SmallVecData::{Inline, Heap}; @@ -510,6 +515,7 @@ impl SmallVec where A::Item: Copy { } } +#[cfg(feature="heapsizeof")] impl HeapSizeOf for SmallVec where A::Item: HeapSizeOf { fn heap_size_of_children(&self) -> usize { match self.data { @@ -853,9 +859,12 @@ impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 3 #[cfg(test)] pub mod tests { use SmallVec; - use heapsize::HeapSizeOf; use std::borrow::ToOwned; use std::iter::FromIterator; + + #[cfg(feature="heapsizeof")] + use heapsize::HeapSizeOf; + #[cfg(feature="heapsizeof")] use std::mem::size_of; // We heap allocate all these strings so that double frees will show up under valgrind. @@ -1279,6 +1288,7 @@ pub mod tests { assert_eq!(&SmallVec::<[u32; 2]>::from_slice(&[1, 2, 3][..])[..], [1, 2, 3]); } + #[cfg(feature="heapsizeof")] #[test] fn test_heap_size_of_children() { let mut vec = SmallVec::<[u32; 2]>::new(); From f61bbaa79ff4da147bff1262c22e11d47729dda0 Mon Sep 17 00:00:00 2001 From: Nipunn Koorapati Date: Thu, 16 Mar 2017 14:39:30 -0700 Subject: [PATCH 4/4] Remove specialization feature --- lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib.rs b/lib.rs index fe1f63f..f2d0776 100644 --- a/lib.rs +++ b/lib.rs @@ -6,8 +6,6 @@ //! to the heap for larger allocations. This can be a useful optimization for improving cache //! locality and reducing allocator traffic for workloads that fit within the inline buffer. -#![feature(specialization)] - #[cfg(feature="heapsizeof")] extern crate heapsize;