Skip to content

Commit efe77ba

Browse files
committed
Auto merge of #66383 - the8472:in-place-iter-collect, r=<try>
Extend vec::FromIterator specialization to collect in-place for some iterator pipelines This extends the existing `vec.into_iter().collect::<Vec>()` specialization to cover more cases, including iterator pipelines that flow from `IntoIter` through `Map`, `Filter`, `Zip` and `Enumerate` and several others. [Benchmark results](https://gist.github.com/the8472/6d999b2d08a2bedf3b93f12112f96e2f) with defaults and opt-level = 3, codegen-units=1. The specialization should be conservative, it is only applied when we know the iterator will fit into the source allocation. Additionally some existing specializations now applied in a few more code-paths.
2 parents a2e8030 + 2d051db commit efe77ba

File tree

15 files changed

+1041
-59
lines changed

15 files changed

+1041
-59
lines changed

src/liballoc/benches/vec.rs

+247-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
use rand::prelude::*;
12
use std::iter::{repeat, FromIterator};
2-
use test::Bencher;
3+
use test::{black_box, Bencher};
34

45
#[bench]
56
fn bench_new(b: &mut Bencher) {
67
b.iter(|| {
78
let v: Vec<u32> = Vec::new();
89
assert_eq!(v.len(), 0);
910
assert_eq!(v.capacity(), 0);
11+
v
1012
})
1113
}
1214

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

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

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

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

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

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

@@ -224,9 +232,24 @@ fn do_bench_push_all(b: &mut Bencher, dst_len: usize, src_len: usize) {
224232
dst.extend_from_slice(&src);
225233
assert_eq!(dst.len(), dst_len + src_len);
226234
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
235+
dst
227236
});
228237
}
229238

239+
#[bench]
240+
fn bench_extend_recycle(b: &mut Bencher) {
241+
let mut data = vec![0; 1000];
242+
243+
b.iter(|| {
244+
let tmp = std::mem::replace(&mut data, Vec::new());
245+
let mut to_extend = black_box(Vec::new());
246+
to_extend.extend(tmp.into_iter());
247+
std::mem::replace(&mut data, black_box(to_extend));
248+
});
249+
250+
black_box(data);
251+
}
252+
230253
#[bench]
231254
fn bench_push_all_0000_0000(b: &mut Bencher) {
232255
do_bench_push_all(b, 0, 0)
@@ -273,6 +296,7 @@ fn do_bench_push_all_move(b: &mut Bencher, dst_len: usize, src_len: usize) {
273296
dst.extend(src.clone());
274297
assert_eq!(dst.len(), dst_len + src_len);
275298
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
299+
dst
276300
});
277301
}
278302

@@ -320,6 +344,7 @@ fn do_bench_clone(b: &mut Bencher, src_len: usize) {
320344
let dst = src.clone();
321345
assert_eq!(dst.len(), src_len);
322346
assert!(dst.iter().enumerate().all(|(i, x)| i == *x));
347+
dst
323348
});
324349
}
325350

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

355380
for _ in 0..times {
356381
dst.clone_from(&src);
357-
358382
assert_eq!(dst.len(), src_len);
359383
assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x));
360384
}
385+
dst
361386
});
362387
}
363388

@@ -480,3 +505,223 @@ fn bench_clone_from_10_0100_0010(b: &mut Bencher) {
480505
fn bench_clone_from_10_1000_0100(b: &mut Bencher) {
481506
do_bench_clone_from(b, 10, 1000, 100)
482507
}
508+
509+
macro_rules! bench_in_place {
510+
(
511+
$($fname:ident, $type:ty , $count:expr, $init: expr);*
512+
) => {
513+
$(
514+
#[bench]
515+
fn $fname(b: &mut Bencher) {
516+
b.iter(|| {
517+
let src: Vec<$type> = black_box(vec![$init; $count]);
518+
let mut sink = src.into_iter()
519+
.enumerate()
520+
.map(|(idx, e)| { (idx as $type) ^ e }).collect::<Vec<$type>>();
521+
black_box(sink.as_mut_ptr())
522+
});
523+
}
524+
)+
525+
};
526+
}
527+
528+
bench_in_place![
529+
bench_in_place_xxu8_i0_0010, u8, 10, 0;
530+
bench_in_place_xxu8_i0_0100, u8, 100, 0;
531+
bench_in_place_xxu8_i0_1000, u8, 1000, 0;
532+
bench_in_place_xxu8_i1_0010, u8, 10, 1;
533+
bench_in_place_xxu8_i1_0100, u8, 100, 1;
534+
bench_in_place_xxu8_i1_1000, u8, 1000, 1;
535+
bench_in_place_xu32_i0_0010, u32, 10, 0;
536+
bench_in_place_xu32_i0_0100, u32, 100, 0;
537+
bench_in_place_xu32_i0_1000, u32, 1000, 0;
538+
bench_in_place_xu32_i1_0010, u32, 10, 1;
539+
bench_in_place_xu32_i1_0100, u32, 100, 1;
540+
bench_in_place_xu32_i1_1000, u32, 1000, 1;
541+
bench_in_place_u128_i0_0010, u128, 10, 0;
542+
bench_in_place_u128_i0_0100, u128, 100, 0;
543+
bench_in_place_u128_i0_1000, u128, 1000, 0;
544+
bench_in_place_u128_i1_0010, u128, 10, 1;
545+
bench_in_place_u128_i1_0100, u128, 100, 1;
546+
bench_in_place_u128_i1_1000, u128, 1000, 1
547+
];
548+
549+
#[bench]
550+
fn bench_in_place_recycle(b: &mut test::Bencher) {
551+
let mut data = vec![0; 1000];
552+
553+
b.iter(|| {
554+
let tmp = std::mem::replace(&mut data, Vec::new());
555+
std::mem::replace(
556+
&mut data,
557+
black_box(
558+
tmp.into_iter()
559+
.enumerate()
560+
.map(|(idx, e)| idx.wrapping_add(e))
561+
.fuse()
562+
.peekable()
563+
.collect::<Vec<usize>>(),
564+
),
565+
);
566+
});
567+
}
568+
569+
#[bench]
570+
fn bench_in_place_zip_recycle(b: &mut test::Bencher) {
571+
let mut data = vec![0u8; 1000];
572+
let mut rng = rand::thread_rng();
573+
let mut subst = vec![0u8; 1000];
574+
rng.fill_bytes(&mut subst[..]);
575+
576+
b.iter(|| {
577+
let tmp = std::mem::replace(&mut data, Vec::new());
578+
let mangled = tmp
579+
.into_iter()
580+
.zip(subst.iter().copied())
581+
.enumerate()
582+
.map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s)
583+
.collect::<Vec<_>>();
584+
assert_eq!(mangled.len(), 1000);
585+
std::mem::replace(&mut data, black_box(mangled));
586+
});
587+
}
588+
589+
#[bench]
590+
fn bench_in_place_zip_iter_mut(b: &mut test::Bencher) {
591+
let mut data = vec![0u8; 256];
592+
let mut rng = rand::thread_rng();
593+
let mut subst = vec![0u8; 1000];
594+
rng.fill_bytes(&mut subst[..]);
595+
596+
b.iter(|| {
597+
data.iter_mut().enumerate().for_each(|(i, d)| {
598+
*d = d.wrapping_add(i as u8) ^ subst[i];
599+
});
600+
});
601+
602+
black_box(data);
603+
}
604+
605+
#[derive(Clone)]
606+
struct Droppable(usize);
607+
608+
impl Drop for Droppable {
609+
fn drop(&mut self) {
610+
black_box(self);
611+
}
612+
}
613+
614+
#[bench]
615+
fn bench_in_place_collect_droppable(b: &mut test::Bencher) {
616+
let v: Vec<Droppable> = std::iter::repeat_with(|| Droppable(0)).take(1000).collect();
617+
b.iter(|| {
618+
v.clone()
619+
.into_iter()
620+
.skip(100)
621+
.enumerate()
622+
.map(|(i, e)| Droppable(i ^ e.0))
623+
.collect::<Vec<_>>()
624+
})
625+
}
626+
627+
#[bench]
628+
fn bench_chain_collect(b: &mut test::Bencher) {
629+
let data = black_box([0; LEN]);
630+
b.iter(|| data.iter().cloned().chain([1].iter().cloned()).collect::<Vec<_>>());
631+
}
632+
633+
#[bench]
634+
fn bench_chain_chain_collect(b: &mut test::Bencher) {
635+
let data = black_box([0; LEN]);
636+
b.iter(|| {
637+
data.iter()
638+
.cloned()
639+
.chain([1].iter().cloned())
640+
.chain([2].iter().cloned())
641+
.collect::<Vec<_>>()
642+
});
643+
}
644+
645+
#[bench]
646+
fn bench_nest_chain_chain_collect(b: &mut test::Bencher) {
647+
let data = black_box([0; LEN]);
648+
b.iter(|| {
649+
data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::<Vec<_>>()
650+
});
651+
}
652+
653+
pub fn example_plain_slow(l: &[u32]) -> Vec<u32> {
654+
let mut result = Vec::with_capacity(l.len());
655+
result.extend(l.iter().rev());
656+
result
657+
}
658+
659+
pub fn map_fast(l: &[(u32, u32)]) -> Vec<u32> {
660+
let mut result = Vec::with_capacity(l.len());
661+
for i in 0..l.len() {
662+
unsafe {
663+
*result.get_unchecked_mut(i) = l[i].0;
664+
result.set_len(i);
665+
}
666+
}
667+
result
668+
}
669+
670+
const LEN: usize = 16384;
671+
672+
#[bench]
673+
fn bench_range_map_collect(b: &mut test::Bencher) {
674+
b.iter(|| (0..LEN).map(|_| u32::default()).collect::<Vec<_>>());
675+
}
676+
677+
#[bench]
678+
fn bench_chain_extend_ref(b: &mut test::Bencher) {
679+
let data = black_box([0; LEN]);
680+
b.iter(|| {
681+
let mut v = Vec::<u32>::with_capacity(data.len() + 1);
682+
v.extend(data.iter().chain([1].iter()));
683+
v
684+
});
685+
}
686+
687+
#[bench]
688+
fn bench_chain_extend_value(b: &mut test::Bencher) {
689+
let data = black_box([0; LEN]);
690+
b.iter(|| {
691+
let mut v = Vec::<u32>::with_capacity(data.len() + 1);
692+
v.extend(data.iter().cloned().chain(Some(1)));
693+
v
694+
});
695+
}
696+
697+
#[bench]
698+
fn bench_rev_1(b: &mut test::Bencher) {
699+
let data = black_box([0; LEN]);
700+
b.iter(|| {
701+
let mut v = Vec::<u32>::new();
702+
v.extend(data.iter().rev());
703+
v
704+
});
705+
}
706+
707+
#[bench]
708+
fn bench_rev_2(b: &mut test::Bencher) {
709+
let data = black_box([0; LEN]);
710+
b.iter(|| example_plain_slow(&data));
711+
}
712+
713+
#[bench]
714+
fn bench_map_regular(b: &mut test::Bencher) {
715+
let data = black_box([(0, 0); LEN]);
716+
b.iter(|| {
717+
let mut v = Vec::<u32>::new();
718+
v.extend(data.iter().map(|t| t.1));
719+
v
720+
});
721+
}
722+
723+
#[bench]
724+
fn bench_map_fast(b: &mut test::Bencher) {
725+
let data = black_box([(0, 0); LEN]);
726+
b.iter(|| map_fast(&data));
727+
}

src/liballoc/collections/binary_heap.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,13 @@
146146
#![stable(feature = "rust1", since = "1.0.0")]
147147

148148
use core::fmt;
149-
use core::iter::{FromIterator, FusedIterator, TrustedLen};
149+
use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
150150
use core::mem::{size_of, swap, ManuallyDrop};
151151
use core::ops::{Deref, DerefMut};
152152
use core::ptr;
153153

154154
use crate::slice;
155-
use crate::vec::{self, Vec};
155+
use crate::vec::{self, AsIntoIter, Vec};
156156

157157
use super::SpecExtend;
158158

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

1148+
#[unstable(issue = "none", feature = "inplace_iteration")]
1149+
unsafe impl<T> SourceIter for IntoIter<T> {
1150+
type Source = IntoIter<T>;
1151+
1152+
#[inline]
1153+
unsafe fn as_inner(&mut self) -> &mut Self::Source {
1154+
self
1155+
}
1156+
}
1157+
1158+
#[unstable(issue = "none", feature = "inplace_iteration")]
1159+
unsafe impl<I> InPlaceIterable for IntoIter<I> {}
1160+
1161+
impl<I> AsIntoIter for IntoIter<I> {
1162+
type Item = I;
1163+
1164+
fn as_into_iter(&mut self) -> &mut vec::IntoIter<Self::Item> {
1165+
&mut self.iter
1166+
}
1167+
}
1168+
11481169
#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
11491170
#[derive(Clone, Debug)]
11501171
pub struct IntoIterSorted<T> {

src/liballoc/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@
121121
#![feature(alloc_layout_extra)]
122122
#![feature(try_trait)]
123123
#![feature(associated_type_bounds)]
124+
#![feature(inplace_iteration)]
125+
#![feature(type_alias_impl_trait)]
126+
#![feature(never_type)]
124127

125128
// Allow testing this library
126129

0 commit comments

Comments
 (0)