Skip to content

Commit 8c9f6c8

Browse files
author
bors-servo
authored
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 -->
2 parents d1a64d4 + 401fb09 commit 8c9f6c8

File tree

1 file changed

+41
-6
lines changed

1 file changed

+41
-6
lines changed

lib.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222

2323
#[cfg(not(feature = "std"))]
24-
#[cfg_attr(test, macro_use)]
24+
#[macro_use]
2525
extern crate alloc;
2626

2727
#[cfg(not(feature = "std"))]
@@ -637,6 +637,10 @@ impl<A: Array> SmallVec<A> {
637637
/// back.
638638
pub fn insert_many<I: IntoIterator<Item=A::Item>>(&mut self, index: usize, iterable: I) {
639639
let iter = iterable.into_iter();
640+
if index == self.len() {
641+
return self.extend(iter);
642+
}
643+
640644
let (lower_size_bound, _) = iter.size_hint();
641645
assert!(lower_size_bound <= std::isize::MAX as usize); // Ensure offset is indexable
642646
assert!(index + lower_size_bound >= index); // Protect against overflow
@@ -805,9 +809,23 @@ impl<A: Array> SmallVec<A> where A::Item: Clone {
805809
/// assert_eq!(v, SmallVec::from_buf(['d', 'd']));
806810
/// ```
807811
pub fn from_elem(elem: A::Item, n: usize) -> Self {
808-
let mut v = SmallVec::with_capacity(n);
809-
v.insert_many(0, (0..n).map(|_| elem.clone()));
810-
v
812+
if n > A::size() {
813+
vec![elem; n].into()
814+
} else {
815+
unsafe {
816+
let mut arr: A = ::std::mem::uninitialized();
817+
let ptr = arr.ptr_mut();
818+
819+
for i in 0..n as isize {
820+
::std::ptr::write(ptr.offset(i), elem.clone());
821+
}
822+
823+
SmallVec {
824+
data: Inline { array: arr },
825+
len: n,
826+
}
827+
}
828+
}
811829
}
812830
}
813831

@@ -1001,13 +1019,30 @@ impl<A: Array> FromIterator<A::Item> for SmallVec<A> {
10011019

10021020
impl<A: Array> Extend<A::Item> for SmallVec<A> {
10031021
fn extend<I: IntoIterator<Item=A::Item>>(&mut self, iterable: I) {
1004-
let iter = iterable.into_iter();
1022+
let mut iter = iterable.into_iter();
10051023
let (lower_size_bound, _) = iter.size_hint();
10061024

10071025
let target_len = self.len + lower_size_bound;
10081026

10091027
if target_len > self.capacity() {
1010-
self.grow(target_len);
1028+
self.grow(target_len);
1029+
}
1030+
1031+
unsafe {
1032+
let ptr = self.as_mut_ptr().offset(self.len() as isize);
1033+
1034+
let len = self.len();
1035+
let mut count = 0;
1036+
for p in 0..lower_size_bound as isize {
1037+
if let Some(out) = iter.next() {
1038+
::std::ptr::write(ptr.offset(p), out);
1039+
count += 1;
1040+
} else {
1041+
break;
1042+
}
1043+
}
1044+
1045+
self.set_len(len + count);
10111046
}
10121047

10131048
for elem in iter {

0 commit comments

Comments
 (0)