Skip to content

Extend vec::FromIterator specialization to collect in-place for some iterator pipelines #66383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 54 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
014c46b
unrelated typo fix
the8472 Oct 11, 2019
fa02f72
bench
the8472 Oct 18, 2019
9141373
in-place collect for Vec. Box<[]> and BinaryHeap IntoIter and some ad…
the8472 Oct 11, 2019
5d4f055
mark SourceIter as unsafe, document invariants
the8472 Nov 15, 2019
f91bee5
assert that SourceIter requirements have not been violated by the pip…
the8472 Nov 15, 2019
dd26d62
implement drop handling
the8472 Nov 15, 2019
af4ebcb
use add instead of offset
the8472 Nov 15, 2019
25dbd19
do as tidy demands
the8472 Nov 15, 2019
331501e
fix doc link
the8472 Nov 15, 2019
b446bd1
simplify pointer arithmetic
the8472 Nov 15, 2019
39ea563
update benches
the8472 Nov 16, 2019
2572f58
recover vectorization
the8472 Nov 16, 2019
1acb718
replace mem::forget with mem::ManuallyDrop
the8472 Nov 16, 2019
b958bea
remove example that relied on non-public trait
the8472 Nov 17, 2019
fc807c4
hide binary_heap::IntoIter internals behind impl Trait
the8472 Nov 17, 2019
6038542
restore Vec::extend specialization for vec::IntoIter sources that
the8472 Nov 17, 2019
997e795
use memmove instead of generic in-place iteration for IntoIter source
the8472 Nov 17, 2019
a6b9be8
add benches from bluss' gists
the8472 Nov 19, 2019
6696ae4
return the things under test so they get black_box()'ed
the8472 Nov 20, 2019
646066c
cyclic in-place reuse bench
the8472 Nov 20, 2019
8749e46
bench in-place collect of droppables
the8472 Nov 20, 2019
a9a2ebc
exercise more of the in-place pipeline in the bench
the8472 Nov 21, 2019
ce827cf
use From specializations on extend if extended Vec is empty
the8472 Nov 21, 2019
441f2d5
restore SpecFrom<T, TrustedLen<Item=T>> specialization by nesting
the8472 Nov 21, 2019
c9fa7ce
specialize creating a Vec from a slice iterator where T: Copy
the8472 Nov 21, 2019
a524321
improve comments
the8472 Nov 21, 2019
14f8894
remove redundant code
the8472 Nov 23, 2019
d077778
fix some in-place-collect edge-cases
the8472 Nov 23, 2019
e207975
additional specializations tests
the8472 Nov 23, 2019
96c049f
bench in-place zip
the8472 Nov 23, 2019
4220852
add in-place iteration for Zip
the8472 Nov 23, 2019
366afd9
move free-standing method into trait impl
the8472 Nov 23, 2019
492a914
support in-place iteration for most adapters
the8472 Nov 23, 2019
0a8027e
make tidy happy
the8472 Nov 23, 2019
1b4da46
remove unecessary feature flag
the8472 Nov 23, 2019
4bb7865
include in-place .zip() in test
the8472 Nov 27, 2019
aa0a12b
bench larger allocations
the8472 Nov 27, 2019
ca61c6b
impl TrustedRandomAccess for vec::IntoIter
the8472 Nov 29, 2019
abf9ff8
fix build issue due to stabilized feature
the8472 Nov 30, 2019
65e240d
fix: bench didn't black_box its results
the8472 Nov 30, 2019
1114f83
avoid exposing that binary heap's IntoIter is backed by vec::IntoIter…
the8472 Dec 20, 2019
ee2330e
mark as_inner as unsafe and update comments
the8472 Jan 18, 2020
8efa793
replace drop flag with ManuallyDrop
the8472 Jan 18, 2020
e300c19
pacify tidy
the8472 Jan 18, 2020
bc7199f
replace unsafe ptr::write with mem::replace, benchmarks show no diffe…
the8472 Jan 25, 2020
c808159
move unsafety into method, not relevant to caller
the8472 Jan 25, 2020
2ad9a4b
test drops during in-place iteration
the8472 Jan 25, 2020
aaa16a0
remove redundant cast
the8472 Jan 25, 2020
96cbc9d
add benchmark to cover in-place extend
the8472 Jan 25, 2020
6cf243a
extract IntoIter drop/forget used by specialization into separate met…
the8472 Jan 26, 2020
089c9c8
work around compiler overhead around lambdas in generics by extractin…
the8472 Jan 26, 2020
0576929
increase comment verbosity
the8472 Jan 26, 2020
a724f9a
generalize in-place collect to types of same size and alignment
the8472 Jan 28, 2020
2d051db
pacify tidy
the8472 Feb 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 247 additions & 2 deletions src/liballoc/benches/vec.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use rand::prelude::*;
use std::iter::{repeat, FromIterator};
use test::Bencher;
use test::{black_box, Bencher};

#[bench]
fn bench_new(b: &mut Bencher) {
b.iter(|| {
let v: Vec<u32> = Vec::new();
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 0);
v
})
}

Expand All @@ -17,6 +19,7 @@ fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) {
let v: Vec<u32> = Vec::with_capacity(src_len);
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), src_len);
v
})
}

Expand Down Expand Up @@ -47,6 +50,7 @@ fn do_bench_from_fn(b: &mut Bencher, src_len: usize) {
let dst = (0..src_len).collect::<Vec<_>>();
assert_eq!(dst.len(), src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
})
}

Expand Down Expand Up @@ -77,6 +81,7 @@ fn do_bench_from_elem(b: &mut Bencher, src_len: usize) {
let dst: Vec<usize> = repeat(5).take(src_len).collect();
assert_eq!(dst.len(), src_len);
assert!(dst.iter().all(|x| *x == 5));
dst
})
}

Expand Down Expand Up @@ -109,6 +114,7 @@ fn do_bench_from_slice(b: &mut Bencher, src_len: usize) {
let dst = src.clone()[..].to_vec();
assert_eq!(dst.len(), src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

Expand Down Expand Up @@ -141,6 +147,7 @@ fn do_bench_from_iter(b: &mut Bencher, src_len: usize) {
let dst: Vec<_> = FromIterator::from_iter(src.clone());
assert_eq!(dst.len(), src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

Expand Down Expand Up @@ -175,6 +182,7 @@ fn do_bench_extend(b: &mut Bencher, dst_len: usize, src_len: usize) {
dst.extend(src.clone());
assert_eq!(dst.len(), dst_len + src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

Expand Down Expand Up @@ -224,9 +232,24 @@ fn do_bench_push_all(b: &mut Bencher, dst_len: usize, src_len: usize) {
dst.extend_from_slice(&src);
assert_eq!(dst.len(), dst_len + src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

#[bench]
fn bench_extend_recycle(b: &mut Bencher) {
let mut data = vec![0; 1000];

b.iter(|| {
let tmp = std::mem::replace(&mut data, Vec::new());
let mut to_extend = black_box(Vec::new());
to_extend.extend(tmp.into_iter());
std::mem::replace(&mut data, black_box(to_extend));
});

black_box(data);
}

#[bench]
fn bench_push_all_0000_0000(b: &mut Bencher) {
do_bench_push_all(b, 0, 0)
Expand Down Expand Up @@ -273,6 +296,7 @@ fn do_bench_push_all_move(b: &mut Bencher, dst_len: usize, src_len: usize) {
dst.extend(src.clone());
assert_eq!(dst.len(), dst_len + src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

Expand Down Expand Up @@ -320,6 +344,7 @@ fn do_bench_clone(b: &mut Bencher, src_len: usize) {
let dst = src.clone();
assert_eq!(dst.len(), src_len);
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
dst
});
}

Expand Down Expand Up @@ -354,10 +379,10 @@ fn do_bench_clone_from(b: &mut Bencher, times: usize, dst_len: usize, src_len: u

for _ in 0..times {
dst.clone_from(&src);

assert_eq!(dst.len(), src_len);
assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x));
}
dst
});
}

Expand Down Expand Up @@ -480,3 +505,223 @@ fn bench_clone_from_10_0100_0010(b: &mut Bencher) {
fn bench_clone_from_10_1000_0100(b: &mut Bencher) {
do_bench_clone_from(b, 10, 1000, 100)
}

macro_rules! bench_in_place {
(
$($fname:ident, $type:ty , $count:expr, $init: expr);*
) => {
$(
#[bench]
fn $fname(b: &mut Bencher) {
b.iter(|| {
let src: Vec<$type> = black_box(vec![$init; $count]);
let mut sink = src.into_iter()
.enumerate()
.map(|(idx, e)| { (idx as $type) ^ e }).collect::<Vec<$type>>();
black_box(sink.as_mut_ptr())
});
}
)+
};
}

bench_in_place![
bench_in_place_xxu8_i0_0010, u8, 10, 0;
bench_in_place_xxu8_i0_0100, u8, 100, 0;
bench_in_place_xxu8_i0_1000, u8, 1000, 0;
bench_in_place_xxu8_i1_0010, u8, 10, 1;
bench_in_place_xxu8_i1_0100, u8, 100, 1;
bench_in_place_xxu8_i1_1000, u8, 1000, 1;
bench_in_place_xu32_i0_0010, u32, 10, 0;
bench_in_place_xu32_i0_0100, u32, 100, 0;
bench_in_place_xu32_i0_1000, u32, 1000, 0;
bench_in_place_xu32_i1_0010, u32, 10, 1;
bench_in_place_xu32_i1_0100, u32, 100, 1;
bench_in_place_xu32_i1_1000, u32, 1000, 1;
bench_in_place_u128_i0_0010, u128, 10, 0;
bench_in_place_u128_i0_0100, u128, 100, 0;
bench_in_place_u128_i0_1000, u128, 1000, 0;
bench_in_place_u128_i1_0010, u128, 10, 1;
bench_in_place_u128_i1_0100, u128, 100, 1;
bench_in_place_u128_i1_1000, u128, 1000, 1
];

#[bench]
fn bench_in_place_recycle(b: &mut test::Bencher) {
let mut data = vec![0; 1000];

b.iter(|| {
let tmp = std::mem::replace(&mut data, Vec::new());
std::mem::replace(
&mut data,
black_box(
tmp.into_iter()
.enumerate()
.map(|(idx, e)| idx.wrapping_add(e))
.fuse()
.peekable()
.collect::<Vec<usize>>(),
),
);
});
}

#[bench]
fn bench_in_place_zip_recycle(b: &mut test::Bencher) {
let mut data = vec![0u8; 1000];
let mut rng = rand::thread_rng();
let mut subst = vec![0u8; 1000];
rng.fill_bytes(&mut subst[..]);

b.iter(|| {
let tmp = std::mem::replace(&mut data, Vec::new());
let mangled = tmp
.into_iter()
.zip(subst.iter().copied())
.enumerate()
.map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s)
.collect::<Vec<_>>();
assert_eq!(mangled.len(), 1000);
std::mem::replace(&mut data, black_box(mangled));
});
}

#[bench]
fn bench_in_place_zip_iter_mut(b: &mut test::Bencher) {
let mut data = vec![0u8; 256];
let mut rng = rand::thread_rng();
let mut subst = vec![0u8; 1000];
rng.fill_bytes(&mut subst[..]);

b.iter(|| {
data.iter_mut().enumerate().for_each(|(i, d)| {
*d = d.wrapping_add(i as u8) ^ subst[i];
});
});

black_box(data);
}

#[derive(Clone)]
struct Droppable(usize);

impl Drop for Droppable {
fn drop(&mut self) {
black_box(self);
}
}

#[bench]
fn bench_in_place_collect_droppable(b: &mut test::Bencher) {
let v: Vec<Droppable> = std::iter::repeat_with(|| Droppable(0)).take(1000).collect();
b.iter(|| {
v.clone()
.into_iter()
.skip(100)
.enumerate()
.map(|(i, e)| Droppable(i ^ e.0))
.collect::<Vec<_>>()
})
}

#[bench]
fn bench_chain_collect(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| data.iter().cloned().chain([1].iter().cloned()).collect::<Vec<_>>());
}

#[bench]
fn bench_chain_chain_collect(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| {
data.iter()
.cloned()
.chain([1].iter().cloned())
.chain([2].iter().cloned())
.collect::<Vec<_>>()
});
}

#[bench]
fn bench_nest_chain_chain_collect(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| {
data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::<Vec<_>>()
});
}

pub fn example_plain_slow(l: &[u32]) -> Vec<u32> {
let mut result = Vec::with_capacity(l.len());
result.extend(l.iter().rev());
result
}

pub fn map_fast(l: &[(u32, u32)]) -> Vec<u32> {
let mut result = Vec::with_capacity(l.len());
for i in 0..l.len() {
unsafe {
*result.get_unchecked_mut(i) = l[i].0;
result.set_len(i);
}
}
result
}

const LEN: usize = 16384;

#[bench]
fn bench_range_map_collect(b: &mut test::Bencher) {
b.iter(|| (0..LEN).map(|_| u32::default()).collect::<Vec<_>>());
}

#[bench]
fn bench_chain_extend_ref(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| {
let mut v = Vec::<u32>::with_capacity(data.len() + 1);
v.extend(data.iter().chain([1].iter()));
v
});
}

#[bench]
fn bench_chain_extend_value(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| {
let mut v = Vec::<u32>::with_capacity(data.len() + 1);
v.extend(data.iter().cloned().chain(Some(1)));
v
});
}

#[bench]
fn bench_rev_1(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| {
let mut v = Vec::<u32>::new();
v.extend(data.iter().rev());
v
});
}

#[bench]
fn bench_rev_2(b: &mut test::Bencher) {
let data = black_box([0; LEN]);
b.iter(|| example_plain_slow(&data));
}

#[bench]
fn bench_map_regular(b: &mut test::Bencher) {
let data = black_box([(0, 0); LEN]);
b.iter(|| {
let mut v = Vec::<u32>::new();
v.extend(data.iter().map(|t| t.1));
v
});
}

#[bench]
fn bench_map_fast(b: &mut test::Bencher) {
let data = black_box([(0, 0); LEN]);
b.iter(|| map_fast(&data));
}
25 changes: 23 additions & 2 deletions src/liballoc/collections/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,13 @@
#![stable(feature = "rust1", since = "1.0.0")]

use core::fmt;
use core::iter::{FromIterator, FusedIterator, TrustedLen};
use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
use core::mem::{size_of, swap, ManuallyDrop};
use core::ops::{Deref, DerefMut};
use core::ptr;

use crate::slice;
use crate::vec::{self, Vec};
use crate::vec::{self, AsIntoIter, Vec};

use super::SpecExtend;

Expand Down Expand Up @@ -1145,6 +1145,27 @@ impl<T> ExactSizeIterator for IntoIter<T> {
#[stable(feature = "fused", since = "1.26.0")]
impl<T> FusedIterator for IntoIter<T> {}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> SourceIter for IntoIter<T> {
type Source = IntoIter<T>;

#[inline]
unsafe fn as_inner(&mut self) -> &mut Self::Source {
self
}
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I> InPlaceIterable for IntoIter<I> {}

impl<I> AsIntoIter for IntoIter<I> {
type Item = I;

fn as_into_iter(&mut self) -> &mut vec::IntoIter<Self::Item> {
&mut self.iter
}
}

#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
#[derive(Clone, Debug)]
pub struct IntoIterSorted<T> {
Expand Down
3 changes: 3 additions & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
#![feature(alloc_layout_extra)]
#![feature(try_trait)]
#![feature(associated_type_bounds)]
#![feature(inplace_iteration)]
#![feature(type_alias_impl_trait)]
#![feature(never_type)]

// Allow testing this library

Expand Down
Loading