Skip to content

Commit

Permalink
perf: Use for_each in Vec::extend
Browse files Browse the repository at this point in the history
`for_each` are specialized for iterators such as `chain` allowing for
faster iteration than a normal `for/while` loop.

Note that since this only checks `size_hint` once at the start it may
end up needing to call `reserve` more in the case that `size_hint`
returns a larger and more accurate lower bound during iteration.

This could maybe be alleviated with an implementation closure like the current
one but the extra complexity will likely end up harming the normal case
of an accurate or 0 (think `filter`) lower bound.

```rust
while let Some(element) = iterator.next() {
    let (lower, _) = iterator.size_hint();
    self.reserve(lower.saturating_add(1));
    unsafe {
        let len = self.len();
        ptr::write(self.get_unchecked_mut(len), element);
        // NB can't overflow since we would have had to alloc the address space
        self.set_len(len + 1);
    }

    iterator.by_ref().take(self.capacity()).for_each(|element| {
        unsafe {
            let len = self.len();
            ptr::write(self.get_unchecked_mut(len), element);
            // NB can't overflow since we would have had to alloc the address space
            self.set_len(len + 1);
        }
    });
}

// OR

let (lower, _) = iterator.size_hint();
self.reserve(lower);
loop {
    let result = iterator.by_ref().try_for_each(|element| {
        if self.len() == self.capacity() {
            return Err(element);
        }
        unsafe {
            let len = self.len();
            ptr::write(self.get_unchecked_mut(len), element);
            // NB can't overflow since we would have had to alloc the address space
            self.set_len(len + 1);
        }
        Ok(())
    });

    match result {
        Ok(()) => break,
        Err(element) => {
            let (lower, _) = iterator.size_hint();
            self.reserve(lower.saturating_add(1));
            self.push(element);
        }
    }
}
```

Closes rust-lang#63340
  • Loading branch information
Marwes authored and Markus Westerlind committed Feb 6, 2020
1 parent 9b0cdeb commit ddabd76
Showing 1 changed file with 5 additions and 13 deletions.
18 changes: 5 additions & 13 deletions src/liballoc/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2122,26 +2122,18 @@ where
}

impl<T> Vec<T> {
fn extend_desugared<I: Iterator<Item = T>>(&mut self, mut iterator: I) {
fn extend_desugared<I: Iterator<Item = T>>(&mut self, iterator: I) {
// This is the case for a general iterator.
//
// This function should be the moral equivalent of:
//
// for item in iterator {
// self.push(item);
// }
while let Some(element) = iterator.next() {
let len = self.len();
if len == self.capacity() {
let (lower, _) = iterator.size_hint();
self.reserve(lower.saturating_add(1));
}
unsafe {
ptr::write(self.get_unchecked_mut(len), element);
// NB can't overflow since we would have had to alloc the address space
self.set_len(len + 1);
}
}
let (lower, _) = iterator.size_hint();
self.reserve(lower);

iterator.for_each(|element| self.push(element));
}

/// Creates a splicing iterator that replaces the specified range in the vector
Expand Down

0 comments on commit ddabd76

Please sign in to comment.