From cc31d1e57da67771cdb008e112ca94b8ba107271 Mon Sep 17 00:00:00 2001 From: Jef Date: Fri, 18 May 2018 11:50:43 +0200 Subject: [PATCH 1/2] Add benchmarks for smallvec when it's on the stack --- benches/bench.rs | 269 ++++++++++++++++++++++++----------------------- lib.rs | 2 +- 2 files changed, 138 insertions(+), 133 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 7196176..9d5fc0b 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,213 +4,218 @@ extern crate smallvec; extern crate test; -use smallvec::SmallVec; use self::test::Bencher; +use smallvec::{ExtendFromSlice, SmallVec}; -#[bench] -fn bench_push(b: &mut Bencher) { - #[inline(never)] - fn push_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) { - vec.push(x) - } +const VEC_SIZE: usize = 16; - b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - for x in 0..100 { - push_noinline(&mut vec, x); - } - vec - }); +trait Vector: for<'a> From<&'a [T]> + Extend + ExtendFromSlice { + fn new() -> Self; + fn push(&mut self, val: T); + fn pop(&mut self) -> Option; + fn insert(&mut self, n: usize, val: T); + fn from_elem(val: T, n: usize) -> Self; } -#[bench] -fn bench_insert(b: &mut Bencher) { - #[inline(never)] - fn insert_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) { - vec.insert(0, x) +impl Vector for Vec { + fn new() -> Self { + Self::with_capacity(VEC_SIZE) + } + fn push(&mut self, val: T) { + self.push(val) + } + fn pop(&mut self) -> Option { + self.pop() + } + fn insert(&mut self, n: usize, val: T) { + self.insert(n, val) + } + fn from_elem(val: T, n: usize) -> Self { + vec![val; n] } - - b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - for x in 0..100 { - insert_noinline(&mut vec, x); - } - vec - }); } -#[bench] -fn bench_insert_many(b: &mut Bencher) { - #[inline(never)] - fn insert_many_noinline>( - vec: &mut SmallVec<[u64; 16]>, index: usize, iterable: I) { - vec.insert_many(index, iterable) +impl Vector for SmallVec<[T; VEC_SIZE]> { + fn new() -> Self { + Self::new() + } + fn push(&mut self, val: T) { + self.push(val) + } + fn pop(&mut self) -> Option { + self.pop() + } + fn insert(&mut self, n: usize, val: T) { + self.insert(n, val) + } + fn from_elem(val: T, n: usize) -> Self { + smallvec![val; n] } - - b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - insert_many_noinline(&mut vec, 0, 0..100); - insert_many_noinline(&mut vec, 0, 0..100); - vec - }); } -#[bench] -fn bench_extend(b: &mut Bencher) { - b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - vec.extend(0..100); - vec - }); +macro_rules! make_benches { + ($typ:ty { $($b_name:ident => $g_name:ident($($args:expr),*),)* }) => { + $( + #[bench] + fn $b_name(b: &mut Bencher) { + $g_name::<$typ>($($args,)* b) + } + )* + } } -#[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 - }); +make_benches! { + SmallVec<[u64; VEC_SIZE]> { + bench_push => gen_push(100), + bench_push_small => gen_push(VEC_SIZE as _), + bench_insert => gen_insert(100), + bench_insert_small => gen_insert(VEC_SIZE as _), + bench_extend => gen_extend(100), + bench_extend_small => gen_extend(VEC_SIZE as _), + bench_from_slice => gen_from_slice(100), + bench_from_slice_small => gen_from_slice(VEC_SIZE as _), + bench_extend_from_slice => gen_extend_from_slice(100), + bench_extend_from_slice_small => gen_extend_from_slice(VEC_SIZE as _), + bench_macro_from_elem => gen_from_elem(100), + bench_macro_from_elem_small => gen_from_elem(VEC_SIZE as _), + bench_pushpop => gen_pushpop(), + } } -#[bench] -fn bench_extend_from_slice(b: &mut Bencher) { - let v: Vec = (0..100).collect(); - b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - vec.extend_from_slice(&v); - vec - }); +make_benches! { + Vec { + bench_push_vec => gen_push(100), + bench_push_vec_small => gen_push(VEC_SIZE as _), + bench_insert_vec => gen_insert(100), + bench_insert_vec_small => gen_insert(VEC_SIZE as _), + bench_extend_vec => gen_extend(100), + bench_extend_vec_small => gen_extend(VEC_SIZE as _), + bench_from_slice_vec => gen_from_slice(100), + bench_from_slice_vec_small => gen_from_slice(VEC_SIZE as _), + bench_extend_from_slice_vec => gen_extend_from_slice(100), + bench_extend_from_slice_vec_small => gen_extend_from_slice(VEC_SIZE as _), + bench_macro_from_elem_vec => gen_from_elem(100), + bench_macro_from_elem_vec_small => gen_from_elem(VEC_SIZE as _), + bench_pushpop_vec => gen_pushpop(), + } } -#[bench] -fn bench_insert_from_slice(b: &mut Bencher) { - let v: Vec = (0..100).collect(); +fn gen_push>(n: u64, b: &mut Bencher) { + #[inline(never)] + fn push_noinline>(vec: &mut V, x: u64) { + vec.push(x); + } + b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - vec.insert_from_slice(0, &v); - vec.insert_from_slice(0, &v); + let mut vec = V::new(); + for x in 0..n { + push_noinline(&mut vec, x); + } vec }); } -#[bench] -fn bench_pushpop(b: &mut Bencher) { +fn gen_insert>(n: u64, b: &mut Bencher) { #[inline(never)] - fn pushpop_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) { - vec.push(x); - vec.pop(); + fn insert_noinline>(vec: &mut V, x: u64) { + vec.insert(0, x) } b.iter(|| { - let mut vec: SmallVec<[u64; 16]> = SmallVec::new(); - for x in 0..100 { - pushpop_noinline(&mut vec, x); + let mut vec = V::new(); + for x in 0..n { + insert_noinline(&mut vec, x); } vec }); } -#[bench] -fn bench_macro_from_elem(b: &mut Bencher) { +fn gen_extend>(n: u64, b: &mut Bencher) { b.iter(|| { - let vec: SmallVec<[u64; 16]> = smallvec![42; 100]; + let mut vec = V::new(); + vec.extend(0..n); vec }); } -#[bench] -fn bench_macro_from_list(b: &mut Bencher) { +fn gen_from_slice>(n: u64, b: &mut Bencher) { + let v: Vec = (0..n).collect(); b.iter(|| { - let vec: SmallVec<[u64; 16]> = smallvec![ - 0, 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 - ]; + let vec = V::from(&v); vec }); } -#[bench] -fn bench_push_vec(b: &mut Bencher) { - #[inline(never)] - fn push_noinline(vec: &mut Vec, x: u64) { - vec.push(x) - } +fn gen_extend_from_slice>(n: u64, b: &mut Bencher) { + let v: Vec = (0..n).collect(); b.iter(|| { - let mut vec: Vec = Vec::with_capacity(16); - for x in 0..100 { - push_noinline(&mut vec, x); - } + let mut vec = V::new(); + vec.extend_from_slice(&v); vec }); } -#[bench] -fn bench_insert_vec(b: &mut Bencher) { +fn gen_pushpop>(b: &mut Bencher) { #[inline(never)] - fn insert_noinline(vec: &mut Vec, x: u64) { - vec.insert(0, x) + fn pushpop_noinline>(vec: &mut V, x: u64) { + vec.push(x); + vec.pop(); } b.iter(|| { - let mut vec: Vec = Vec::with_capacity(16); + let mut vec = V::new(); for x in 0..100 { - insert_noinline(&mut vec, x); + pushpop_noinline(&mut vec, x); } vec }); } -#[bench] -fn bench_extend_vec(b: &mut Bencher) { +fn gen_from_elem>(n: usize, b: &mut Bencher) { b.iter(|| { - let mut vec: Vec = Vec::with_capacity(16); - vec.extend(0..100); + let vec = V::from_elem(42, n); vec }); } #[bench] -fn bench_from_slice_vec(b: &mut Bencher) { - let v: Vec = (0..100).collect(); - b.iter(|| { - let vec: Vec = Vec::from(&v[..]); - vec - }); -} +fn bench_insert_many(b: &mut Bencher) { + #[inline(never)] + fn insert_many_noinline>( + vec: &mut SmallVec<[u64; VEC_SIZE]>, + index: usize, + iterable: I, + ) { + vec.insert_many(index, iterable) + } -#[bench] -fn bench_extend_from_slice_vec(b: &mut Bencher) { - let v: Vec = (0..100).collect(); b.iter(|| { - let mut vec: Vec = Vec::with_capacity(16); - vec.extend_from_slice(&v); + let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); + insert_many_noinline(&mut vec, 0, 0..100); + insert_many_noinline(&mut vec, 0, 0..100); vec }); } #[bench] -fn bench_pushpop_vec(b: &mut Bencher) { - #[inline(never)] - fn pushpop_noinline(vec: &mut Vec, x: u64) { - vec.push(x); - vec.pop(); - } - +fn bench_insert_from_slice(b: &mut Bencher) { + let v: Vec = (0..100).collect(); b.iter(|| { - let mut vec: Vec = Vec::with_capacity(16); - for x in 0..100 { - pushpop_noinline(&mut vec, x); - } + let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); + vec.insert_from_slice(0, &v); + vec.insert_from_slice(0, &v); vec }); } #[bench] -fn bench_macro_from_elem_vec(b: &mut Bencher) { +fn bench_macro_from_list(b: &mut Bencher) { b.iter(|| { - let vec: Vec = vec![42; 100]; + let vec: SmallVec<[u64; 16]> = smallvec![ + 0, 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, + ]; vec }); } @@ -221,7 +226,7 @@ fn bench_macro_from_list_vec(b: &mut Bencher) { let vec: Vec = vec![ 0, 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 + 0x80000, 0x100000, ]; vec }); diff --git a/lib.rs b/lib.rs index 05fc4da..2869548 100644 --- a/lib.rs +++ b/lib.rs @@ -99,7 +99,7 @@ macro_rules! smallvec { ($elem:expr; $n:expr) => ({ SmallVec::from_elem($elem, $n) }); - ($($x:expr),*) => ({ + ($($x:expr),*$(,)*) => ({ SmallVec::from_slice(&[$($x),*]) }); } From 25255c82b1e8ad919ceb81af60564f91c681b222 Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 22 May 2018 13:17:18 +0200 Subject: [PATCH 2/2] Roll in improvements from #94 --- benches/bench.rs | 83 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 9d5fc0b..7380473 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -8,11 +8,13 @@ use self::test::Bencher; use smallvec::{ExtendFromSlice, SmallVec}; const VEC_SIZE: usize = 16; +const SPILLED_SIZE: usize = 100; trait Vector: for<'a> From<&'a [T]> + Extend + ExtendFromSlice { fn new() -> Self; fn push(&mut self, val: T); fn pop(&mut self) -> Option; + fn remove(&mut self, p: usize) -> T; fn insert(&mut self, n: usize, val: T); fn from_elem(val: T, n: usize) -> Self; } @@ -21,15 +23,23 @@ impl Vector for Vec { fn new() -> Self { Self::with_capacity(VEC_SIZE) } + fn push(&mut self, val: T) { self.push(val) } + fn pop(&mut self) -> Option { self.pop() } + + fn remove(&mut self, p: usize) -> T { + self.remove(p) + } + fn insert(&mut self, n: usize, val: T) { self.insert(n, val) } + fn from_elem(val: T, n: usize) -> Self { vec![val; n] } @@ -39,15 +49,23 @@ impl Vector for SmallVec<[T; VEC_SIZE]> { fn new() -> Self { Self::new() } + fn push(&mut self, val: T) { self.push(val) } + fn pop(&mut self) -> Option { self.pop() } + + fn remove(&mut self, p: usize) -> T { + self.remove(p) + } + fn insert(&mut self, n: usize, val: T) { self.insert(n, val) } + fn from_elem(val: T, n: usize) -> Self { smallvec![val; n] } @@ -66,17 +84,19 @@ macro_rules! make_benches { make_benches! { SmallVec<[u64; VEC_SIZE]> { - bench_push => gen_push(100), + bench_push => gen_push(SPILLED_SIZE as _), bench_push_small => gen_push(VEC_SIZE as _), - bench_insert => gen_insert(100), + bench_insert => gen_insert(SPILLED_SIZE as _), bench_insert_small => gen_insert(VEC_SIZE as _), - bench_extend => gen_extend(100), + bench_remove => gen_remove(SPILLED_SIZE as _), + bench_remove_small => gen_remove(VEC_SIZE as _), + bench_extend => gen_extend(SPILLED_SIZE as _), bench_extend_small => gen_extend(VEC_SIZE as _), - bench_from_slice => gen_from_slice(100), + bench_from_slice => gen_from_slice(SPILLED_SIZE as _), bench_from_slice_small => gen_from_slice(VEC_SIZE as _), - bench_extend_from_slice => gen_extend_from_slice(100), + bench_extend_from_slice => gen_extend_from_slice(SPILLED_SIZE as _), bench_extend_from_slice_small => gen_extend_from_slice(VEC_SIZE as _), - bench_macro_from_elem => gen_from_elem(100), + bench_macro_from_elem => gen_from_elem(SPILLED_SIZE as _), bench_macro_from_elem_small => gen_from_elem(VEC_SIZE as _), bench_pushpop => gen_pushpop(), } @@ -84,17 +104,19 @@ make_benches! { make_benches! { Vec { - bench_push_vec => gen_push(100), + bench_push_vec => gen_push(SPILLED_SIZE as _), bench_push_vec_small => gen_push(VEC_SIZE as _), - bench_insert_vec => gen_insert(100), + bench_insert_vec => gen_insert(SPILLED_SIZE as _), bench_insert_vec_small => gen_insert(VEC_SIZE as _), - bench_extend_vec => gen_extend(100), + bench_remove_vec => gen_remove(SPILLED_SIZE as _), + bench_remove_vec_small => gen_remove(VEC_SIZE as _), + bench_extend_vec => gen_extend(SPILLED_SIZE as _), bench_extend_vec_small => gen_extend(VEC_SIZE as _), - bench_from_slice_vec => gen_from_slice(100), + bench_from_slice_vec => gen_from_slice(SPILLED_SIZE as _), bench_from_slice_vec_small => gen_from_slice(VEC_SIZE as _), - bench_extend_from_slice_vec => gen_extend_from_slice(100), + bench_extend_from_slice_vec => gen_extend_from_slice(SPILLED_SIZE as _), bench_extend_from_slice_vec_small => gen_extend_from_slice(VEC_SIZE as _), - bench_macro_from_elem_vec => gen_from_elem(100), + bench_macro_from_elem_vec => gen_from_elem(SPILLED_SIZE as _), bench_macro_from_elem_vec_small => gen_from_elem(VEC_SIZE as _), bench_pushpop_vec => gen_pushpop(), } @@ -117,19 +139,38 @@ fn gen_push>(n: u64, b: &mut Bencher) { fn gen_insert>(n: u64, b: &mut Bencher) { #[inline(never)] - fn insert_noinline>(vec: &mut V, x: u64) { - vec.insert(0, x) + fn insert_noinline>(vec: &mut V, p: usize, x: u64) { + vec.insert(p, x) } b.iter(|| { let mut vec = V::new(); + // Add one element, with each iteration we insert one before the end. + // This means that we benchmark the insertion operation and not the + // time it takes to `ptr::copy` the data. + vec.push(0); for x in 0..n { - insert_noinline(&mut vec, x); + insert_noinline(&mut vec, x as _, x); } vec }); } +fn gen_remove>(n: usize, b: &mut Bencher) { + #[inline(never)] + fn remove_noinline>(vec: &mut V, p: usize) { + vec.remove(p); + } + + b.iter(|| { + let mut vec = V::from_elem(0, n as _); + + for x in (0..n - 1).rev() { + remove_noinline(&mut vec, x) + } + }); +} + fn gen_extend>(n: u64, b: &mut Bencher) { b.iter(|| { let mut vec = V::new(); @@ -157,14 +198,14 @@ fn gen_extend_from_slice>(n: u64, b: &mut Bencher) { fn gen_pushpop>(b: &mut Bencher) { #[inline(never)] - fn pushpop_noinline>(vec: &mut V, x: u64) { + fn pushpop_noinline>(vec: &mut V, x: u64) -> Option { vec.push(x); - vec.pop(); + vec.pop() } b.iter(|| { let mut vec = V::new(); - for x in 0..100 { + for x in 0..SPILLED_SIZE as _ { pushpop_noinline(&mut vec, x); } vec @@ -191,15 +232,15 @@ fn bench_insert_many(b: &mut Bencher) { b.iter(|| { let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); - insert_many_noinline(&mut vec, 0, 0..100); - insert_many_noinline(&mut vec, 0, 0..100); + insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _); + insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _); vec }); } #[bench] fn bench_insert_from_slice(b: &mut Bencher) { - let v: Vec = (0..100).collect(); + let v: Vec = (0..SPILLED_SIZE as _).collect(); b.iter(|| { let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); vec.insert_from_slice(0, &v);