Skip to content

Commit f6ec5b2

Browse files
authored
Micro optimize extend (#362)
Improve code generation by only advancing the iterator in 2 locations
1 parent 72ff74d commit f6ec5b2

File tree

1 file changed

+20
-64
lines changed

1 file changed

+20
-64
lines changed

src/lib.rs

+20-64
Original file line numberDiff line numberDiff line change
@@ -964,20 +964,6 @@ impl<T, const N: usize> SmallVec<T, N> {
964964
unsafe { self.set_len(len + 1) }
965965
}
966966

967-
#[inline]
968-
unsafe fn push_heap(&mut self, value: T) {
969-
// SAFETY: see above
970-
debug_assert!(self.spilled());
971-
let len = self.len();
972-
let cap = self.raw.heap.1;
973-
if len == cap {
974-
self.reserve(1);
975-
}
976-
let ptr = self.raw.heap.0;
977-
ptr.as_ptr().add(len).write(value);
978-
self.set_len(len + 1)
979-
}
980-
981967
#[inline]
982968
pub fn pop(&mut self) -> Option<T> {
983969
if self.is_empty() {
@@ -1502,39 +1488,33 @@ impl<T, const N: usize> SmallVec<T, N> {
15021488

15031489
fn extend_impl<I: Iterator<Item = T>>(&mut self, iter: I) {
15041490
let mut iter = iter.fuse();
1505-
let len = self.len();
15061491
let (lower_bound, _) = iter.size_hint();
15071492
self.reserve(lower_bound);
1508-
let capacity = self.capacity();
1509-
unsafe {
1510-
let ptr = self.as_mut_ptr();
1511-
// SAFETY: ptr is valid for `capacity - len` writes
1512-
let count = extend_batch(ptr, capacity - len, len, &mut iter);
1513-
self.set_len(len + count);
1514-
}
1515-
1516-
if let Some(item) = iter.next() {
1517-
self.push(item);
1518-
} else {
1519-
return;
1520-
}
1521-
1522-
// either we ran out of items, in which case this loop doesn't get executed. or we still
1523-
// have items to push, and in that case we must be on the heap, since we filled up the
1524-
// capacity and then pushed one item
1493+
let mut len = self.len();
1494+
let mut capacity = self.capacity();
1495+
let mut ptr = self.as_mut_ptr();
15251496
unsafe {
15261497
loop {
1498+
// SAFETY: ptr is valid for `capacity - len` writes
1499+
ptr = ptr.add(len);
1500+
let mut guard = DropGuard { ptr, len: 0 };
1501+
iter.by_ref().take(capacity - len).for_each(|item| {
1502+
ptr.add(guard.len).write(item);
1503+
guard.len += 1;
1504+
});
1505+
len += guard.len;
1506+
core::mem::forget(guard);
1507+
self.set_len(len);
1508+
// At this point we either consumed all capacity or the iterator is exhausted (fused)
15271509
if let Some(item) = iter.next() {
1528-
self.push_heap(item);
1510+
self.push(item);
15291511
} else {
1530-
break;
1512+
return;
15311513
}
1532-
let len = self.len();
1533-
let (ptr, capacity) = self.raw.heap;
1534-
let ptr = ptr.as_ptr();
1535-
// SAFETY: ptr is valid for `capacity - len` writes
1536-
let count = extend_batch(ptr, capacity - len, len, &mut iter);
1537-
self.set_len(len + count);
1514+
// SAFETY: The push above would have spilled it
1515+
let (heap_ptr, heap_capacity) = self.raw.heap;
1516+
ptr = heap_ptr.as_ptr();
1517+
capacity = heap_capacity;
15381518
}
15391519
}
15401520
}
@@ -1703,30 +1683,6 @@ unsafe fn insert_many_batch<T, I: Iterator<Item = T>>(
17031683
count
17041684
}
17051685

1706-
// `ptr..ptr + remaining_capacity` must be valid for writes
1707-
#[inline]
1708-
unsafe fn extend_batch<T, I: Iterator<Item = T>>(
1709-
ptr: *mut T,
1710-
remaining_capacity: usize,
1711-
len: usize,
1712-
iter: &mut I,
1713-
) -> usize {
1714-
let ptr_end = ptr.add(len);
1715-
let mut guard = DropGuard {
1716-
ptr: ptr_end,
1717-
len: 0,
1718-
};
1719-
iter.take(remaining_capacity)
1720-
.enumerate()
1721-
.for_each(|(i, item)| {
1722-
ptr_end.add(i).write(item);
1723-
guard.len = i + 1;
1724-
});
1725-
let count = guard.len;
1726-
core::mem::forget(guard);
1727-
count
1728-
}
1729-
17301686
impl<T, const N: usize> Extend<T> for SmallVec<T, N> {
17311687
#[inline]
17321688
fn extend<I: IntoIterator<Item = T>>(&mut self, iterable: I) {

0 commit comments

Comments
 (0)