Auto merge of #93 - Vurich:extend-opts, r=mbrubeck

Optimize `extend` and `from_elem`

This increases the amount of unsafe code, and so should be carefully checked. However, the use of unsafe code is quite benign and depends only on the invariant that `grow` always succeeds (which is depended on other places in the codebase)

Benchcmp results:

```
 name                     preopt.bench ns/iter  postopt.bench ns/iter  diff ns/iter   diff %  speedup
 bench_extend             277                   89                             -188  -67.87%   x 3.11
 bench_macro_from_elem    214                   61                             -153  -71.50%   x 3.51
```

I took a crack at this after noticing how awful the generated assembly for `SmallVec::from_elem` is even for really simple types like integers. It's still not great but it at least now for the case of `Copy` types it gets autovectorised. Probably someone could make it even better if they spent more time on it.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-smallvec/93)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo
2018-05-07 15:07:57 -04:00
committed by GitHub
+41 -6
View File
@@ -21,7 +21,7 @@
#[cfg(not(feature = "std"))]
#[cfg_attr(test, macro_use)]
#[macro_use]
extern crate alloc;
#[cfg(not(feature = "std"))]
@@ -637,6 +637,10 @@ impl<A: Array> SmallVec<A> {
/// back.
pub fn insert_many<I: IntoIterator<Item=A::Item>>(&mut self, index: usize, iterable: I) {
let iter = iterable.into_iter();
if index == self.len() {
return self.extend(iter);
}
let (lower_size_bound, _) = iter.size_hint();
assert!(lower_size_bound <= std::isize::MAX as usize); // Ensure offset is indexable
assert!(index + lower_size_bound >= index); // Protect against overflow
@@ -805,9 +809,23 @@ impl<A: Array> SmallVec<A> where A::Item: Clone {
/// assert_eq!(v, SmallVec::from_buf(['d', 'd']));
/// ```
pub fn from_elem(elem: A::Item, n: usize) -> Self {
let mut v = SmallVec::with_capacity(n);
v.insert_many(0, (0..n).map(|_| elem.clone()));
v
if n > A::size() {
vec![elem; n].into()
} else {
unsafe {
let mut arr: A = ::std::mem::uninitialized();
let ptr = arr.ptr_mut();
for i in 0..n as isize {
::std::ptr::write(ptr.offset(i), elem.clone());
}
SmallVec {
data: Inline { array: arr },
len: n,
}
}
}
}
}
@@ -1001,13 +1019,30 @@ impl<A: Array> FromIterator<A::Item> for SmallVec<A> {
impl<A: Array> Extend<A::Item> for SmallVec<A> {
fn extend<I: IntoIterator<Item=A::Item>>(&mut self, iterable: I) {
let iter = iterable.into_iter();
let mut iter = iterable.into_iter();
let (lower_size_bound, _) = iter.size_hint();
let target_len = self.len + lower_size_bound;
if target_len > self.capacity() {
self.grow(target_len);
self.grow(target_len);
}
unsafe {
let ptr = self.as_mut_ptr().offset(self.len() as isize);
let len = self.len();
let mut count = 0;
for p in 0..lower_size_bound as isize {
if let Some(out) = iter.next() {
::std::ptr::write(ptr.offset(p), out);
count += 1;
} else {
break;
}
}
self.set_len(len + count);
}
for elem in iter {