Skip to content
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

specialize some collection and iterator operations to run in-place #70793

Merged
merged 63 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
076417e
unrelated typo fix
the8472 Oct 11, 2019
038394a
bench
the8472 Oct 18, 2019
bb2d533
in-place collect for Vec. Box<[]> and BinaryHeap IntoIter and some ad…
the8472 Oct 11, 2019
2a32739
mark SourceIter as unsafe, document invariants
the8472 Nov 15, 2019
73a982e
assert that SourceIter requirements have not been violated by the pip…
the8472 Nov 15, 2019
88b7ae6
implement drop handling
the8472 Nov 15, 2019
328a75f
use add instead of offset
the8472 Nov 15, 2019
f904d03
fix doc link
the8472 Nov 15, 2019
bead910
simplify pointer arithmetic
the8472 Nov 15, 2019
6c5c47b
update benches
the8472 Nov 16, 2019
2320650
recover vectorization
the8472 Nov 16, 2019
b90816d
remove example that relied on non-public trait
the8472 Nov 17, 2019
07a8c1b
hide binary_heap::IntoIter internals behind impl Trait
the8472 Nov 17, 2019
631543d
restore Vec::extend specialization for vec::IntoIter sources that
the8472 Nov 17, 2019
a4e385a
use memmove instead of generic in-place iteration for IntoIter source
the8472 Nov 17, 2019
2f700d0
add benches from bluss' gists
the8472 Nov 19, 2019
bb4f888
return the things under test so they get black_box()'ed
the8472 Nov 20, 2019
8ac96e6
cyclic in-place reuse bench
the8472 Nov 20, 2019
a9c78e3
bench in-place collect of droppables
the8472 Nov 20, 2019
a596ff3
exercise more of the in-place pipeline in the bench
the8472 Nov 21, 2019
582fbb1
use From specializations on extend if extended Vec is empty
the8472 Nov 21, 2019
dac0edf
restore SpecFrom<T, TrustedLen<Item=T>> specialization by nesting
the8472 Nov 21, 2019
290fe89
specialize creating a Vec from a slice iterator where T: Copy
the8472 Nov 21, 2019
cc67c8e
improve comments
the8472 Nov 21, 2019
8c816b9
remove redundant code
the8472 Nov 23, 2019
00a32eb
fix some in-place-collect edge-cases
the8472 Nov 23, 2019
2b0b2ae
additional specializations tests
the8472 Nov 23, 2019
3d5e9f1
bench in-place zip
the8472 Nov 23, 2019
0f122e1
add in-place iteration for Zip
the8472 Nov 23, 2019
085eb20
move free-standing method into trait impl
the8472 Nov 23, 2019
21a17d1
support in-place iteration for most adapters
the8472 Nov 23, 2019
70293c6
make tidy happy
the8472 Nov 23, 2019
fbb3371
remove unecessary feature flag
the8472 Aug 24, 2020
fd16202
include in-place .zip() in test
the8472 Nov 27, 2019
e115184
bench larger allocations
the8472 Nov 27, 2019
e85cfa4
impl TrustedRandomAccess for vec::IntoIter
the8472 Nov 29, 2019
0856771
fix build issue due to stabilized feature
the8472 Nov 30, 2019
c731648
fix: bench didn't black_box its results
the8472 Nov 30, 2019
2a51e57
avoid exposing that binary heap's IntoIter is backed by vec::IntoIter…
the8472 Dec 20, 2019
ab382b7
mark as_inner as unsafe and update comments
the8472 Jan 18, 2020
6ed05fd
replace drop flag with ManuallyDrop
the8472 Jan 18, 2020
9596e5a
pacify tidy
the8472 Jul 15, 2020
0d2d033
replace unsafe ptr::write with deref-write, benchmarks show no differ…
the8472 Jan 25, 2020
fe350dd
move unsafety into method, not relevant to caller
the8472 Jan 25, 2020
470bf54
test drops during in-place iteration
the8472 Jan 25, 2020
a7a8b52
remove redundant cast
the8472 Jan 25, 2020
6ad1334
add benchmark to cover in-place extend
the8472 Jan 25, 2020
771b8ec
extract IntoIter drop/forget used by specialization into separate met…
the8472 Jan 26, 2020
872ab78
work around compiler overhead around lambdas in generics by extractin…
the8472 Jan 26, 2020
fa34b39
increase comment verbosity
the8472 Jan 26, 2020
5530858
generalize in-place collect to types of same size and alignment
the8472 Jan 28, 2020
55d1296
pacify tidy
the8472 Feb 2, 2020
8063833
support in-place collect for MapWhile adapters
the8472 Apr 4, 2020
bec9f92
apply required min_specialization attributes
the8472 May 18, 2020
a62cd1b
fix benchmark compile errors
the8472 May 20, 2020
7badb7a
avoid applying in-place collect specialization in type-length test
the8472 May 24, 2020
9aeea00
get things to work under min_specialization by leaning more heavily o…
the8472 Aug 21, 2020
7492f76
please tidy
the8472 Aug 21, 2020
435219d
remove empty Vec extend optimization
the8472 Aug 26, 2020
acdd441
remove separate no-drop code path since it resulted in more LLVM IR
the8472 Aug 26, 2020
6464586
add explanation to specialization marker
the8472 Aug 30, 2020
8e5fe55
improve comments and naming
the8472 Sep 2, 2020
2f23a0f
fix debug assertion
the8472 Sep 3, 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
245 changes: 243 additions & 2 deletions library/alloc/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_extend_from_slice(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());
data = black_box(to_extend);
});

black_box(data);
}

#[bench]
fn bench_extend_from_slice_0000_0000(b: &mut Bencher) {
do_bench_extend_from_slice(b, 0, 0)
Expand Down Expand Up @@ -271,6 +294,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 @@ -305,10 +329,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 @@ -431,3 +455,220 @@ 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());
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);
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 library/alloc/src/collections/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,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::{self, 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 @@ -1173,6 +1173,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
4 changes: 4 additions & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,15 @@
#![feature(fmt_internals)]
#![feature(fn_traits)]
#![feature(fundamental)]
#![feature(inplace_iteration)]
#![feature(internal_uninit_const)]
#![feature(lang_items)]
#![feature(layout_for_ptr)]
#![feature(libc)]
#![feature(map_first_last)]
#![feature(map_into_keys_values)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(new_uninit)]
#![feature(nll)]
#![feature(nonnull_slice_from_raw_parts)]
Expand Down Expand Up @@ -133,7 +135,9 @@
#![feature(slice_partition_dedup)]
#![feature(maybe_uninit_extra, maybe_uninit_slice)]
#![feature(alloc_layout_extra)]
#![feature(trusted_random_access)]
#![feature(try_trait)]
#![feature(type_alias_impl_trait)]
#![feature(associated_type_bounds)]
// Allow testing this library

Expand Down
Loading