Skip to content

Commit

Permalink
Auto merge of rust-lang#95904 - paolobarbolini:vecdeque-specextend, r…
Browse files Browse the repository at this point in the history
…=the8472

Add VecDeque::extend from vec::IntoIter and slice::Iter specializations

Inspired from the [`Vec` `SpecExtend` implementation](https://github.com/rust-lang/rust/blob/027a232755fa9728e9699337267f6675dfd0a8ba/library/alloc/src/vec/spec_extend.rs), but without the specialization for `TrustedLen` which I'll look into in the future.

Should help rust-lang#95632 and KillingSpark/zstd-rs#17

## Benchmarks

Before

```
test vec_deque::bench_extend_bytes    ... bench:         862 ns/iter (+/- 10)
test vec_deque::bench_extend_vec      ... bench:         883 ns/iter (+/- 19)
```

After

```
test vec_deque::bench_extend_bytes    ... bench:           8 ns/iter (+/- 0)
test vec_deque::bench_extend_vec      ... bench:          24 ns/iter (+/- 1)

```
  • Loading branch information
bors committed Apr 28, 2022
2 parents 71cd460 + c126f7f commit 3bfeffd
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 20 deletions.
24 changes: 24 additions & 0 deletions library/alloc/benches/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,27 @@ fn bench_from_array_1000(b: &mut Bencher) {
black_box(deq);
})
}

#[bench]
fn bench_extend_bytes(b: &mut Bencher) {
let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
let input: &[u8] = &[128; 512];

b.iter(|| {
ring.clear();
ring.extend(black_box(input));
});
}

#[bench]
fn bench_extend_vec(b: &mut Bencher) {
let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
let input = vec![128; 512];

b.iter(|| {
ring.clear();

let input = input.clone();
ring.extend(black_box(input));
});
}
25 changes: 6 additions & 19 deletions library/alloc/src/collections/vec_deque/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ use self::ring_slices::RingSlices;

mod ring_slices;

use self::spec_extend::SpecExtend;

mod spec_extend;

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -2970,24 +2974,7 @@ impl<'a, T, A: Allocator> IntoIterator for &'a mut VecDeque<T, A> {
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, A: Allocator> Extend<T> for VecDeque<T, A> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
// This function should be the moral equivalent of:
//
// for item in iter.into_iter() {
// self.push_back(item);
// }
let mut iter = iter.into_iter();
while let Some(element) = iter.next() {
if self.len() == self.capacity() {
let (lower, _) = iter.size_hint();
self.reserve(lower.saturating_add(1));
}

let head = self.head;
self.head = self.wrap_add(self.head, 1);
unsafe {
self.buffer_write(head, element);
}
}
<Self as SpecExtend<T, I::IntoIter>>::spec_extend(self, iter.into_iter());
}

#[inline]
Expand All @@ -3004,7 +2991,7 @@ impl<T, A: Allocator> Extend<T> for VecDeque<T, A> {
#[stable(feature = "extend_ref", since = "1.2.0")]
impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> {
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
self.extend(iter.into_iter().cloned());
self.spec_extend(iter.into_iter());
}

#[inline]
Expand Down
73 changes: 73 additions & 0 deletions library/alloc/src/collections/vec_deque/spec_extend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::alloc::Allocator;
use crate::vec;
use core::slice;

use super::VecDeque;

// Specialization trait used for VecDeque::extend
pub(super) trait SpecExtend<T, I> {
fn spec_extend(&mut self, iter: I);
}

impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A>
where
I: Iterator<Item = T>,
{
default fn spec_extend(&mut self, mut iter: I) {
// This function should be the moral equivalent of:
//
// for item in iter {
// self.push_back(item);
// }
while let Some(element) = iter.next() {
if self.len() == self.capacity() {
let (lower, _) = iter.size_hint();
self.reserve(lower.saturating_add(1));
}

let head = self.head;
self.head = self.wrap_add(self.head, 1);
unsafe {
self.buffer_write(head, element);
}
}
}
}

impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> {
fn spec_extend(&mut self, mut iterator: vec::IntoIter<T>) {
let slice = iterator.as_slice();
self.reserve(slice.len());

unsafe {
self.copy_slice(self.head, slice);
self.head = self.wrap_add(self.head, slice.len());
}
iterator.forget_remaining_elements();
}
}

impl<'a, T: 'a, I, A: Allocator> SpecExtend<&'a T, I> for VecDeque<T, A>
where
I: Iterator<Item = &'a T>,
T: Copy,
{
default fn spec_extend(&mut self, iterator: I) {
self.spec_extend(iterator.copied())
}
}

impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for VecDeque<T, A>
where
T: Copy,
{
fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) {
let slice = iterator.as_slice();
self.reserve(slice.len());

unsafe {
self.copy_slice(self.head, slice);
self.head = self.wrap_add(self.head, slice.len());
}
}
}
5 changes: 5 additions & 0 deletions library/alloc/src/vec/into_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ impl<T, A: Allocator> IntoIter<T, A> {
ptr::drop_in_place(remaining);
}
}

/// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
pub(crate) fn forget_remaining_elements(&mut self) {
self.ptr = self.end;
}
}

#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")]
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/vec/spec_extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl<T, A: Allocator> SpecExtend<T, IntoIter<T>> for Vec<T, A> {
unsafe {
self.append_elements(iterator.as_slice() as _);
}
iterator.ptr = iterator.end;
iterator.forget_remaining_elements();
}
}

Expand Down

0 comments on commit 3bfeffd

Please sign in to comment.