From fd175a88ac232caff25052b852c5f4509a87d170 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 9 Jan 2020 09:04:23 +0100 Subject: [PATCH] perf: Use `for_each` in `Vec::extend` `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 #63340 --- src/liballoc/vec.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index f5f8d88829f5f..fd59c99e258dd 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2119,7 +2119,7 @@ where } impl Vec { - fn extend_desugared>(&mut self, mut iterator: I) { + fn extend_desugared>(&mut self, iterator: I) { // This is the case for a general iterator. // // This function should be the moral equivalent of: @@ -2127,18 +2127,10 @@ impl Vec { // 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