Skip to content

Commit 793ad30

Browse files
committed
FEAT: Improve .extend() performance
We have to use the "SetLenOnDrop" pattern (see stdlib Vec) here. Keep the length in a separate variable, write it back on scope exit. To help the compiler with alias analysis and stuff. We update the length to handle panic in the iteration of the user's iterator, without dropping any elements on the floor. Note: This code was tested without the scope guard using the new option -Zmutable-noalias, which had no effect here. benchmark: ``` name before.txt ns/iter after.txt ns/iter diff ns/iter diff % extend_with_constant 280 (1828 MB/s) 74 (6918 MB/s) -206 -73.57% extend_with_range 1,285 (398 MB/s) 979 (522 MB/s) -306 -23.81% extend_with_slice 29 (17655 MB/s) 14 (36571 MB/s) -15 -51.72% ```
1 parent af8b746 commit 793ad30

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

src/lib.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,21 @@ impl<'a, A: Array> Drop for Drain<'a, A>
805805
}
806806
}
807807

808+
struct ScopeExitGuard<T, Data, F>
809+
where F: FnMut(&Data, &mut T)
810+
{
811+
value: T,
812+
data: Data,
813+
f: F,
814+
}
815+
816+
impl<T, Data, F> Drop for ScopeExitGuard<T, Data, F>
817+
where F: FnMut(&Data, &mut T)
818+
{
819+
fn drop(&mut self) {
820+
(self.f)(&self.data, &mut self.value)
821+
}
822+
}
808823

809824

810825

@@ -815,9 +830,26 @@ impl<'a, A: Array> Drop for Drain<'a, A>
815830
impl<A: Array> Extend<A::Item> for ArrayVec<A> {
816831
fn extend<T: IntoIterator<Item=A::Item>>(&mut self, iter: T) {
817832
let take = self.capacity() - self.len();
818-
for elt in iter.into_iter().take(take) {
819-
unsafe {
820-
self.push_unchecked(elt);
833+
unsafe {
834+
let len = self.len();
835+
let mut ptr = self.as_mut_ptr().offset(len as isize);
836+
// Keep the length in a separate variable, write it back on scope
837+
// exit. To help the compiler with alias analysis and stuff.
838+
// We update the length to handle panic in the iteration of the
839+
// user's iterator, without dropping any elements on the floor.
840+
let mut guard = ScopeExitGuard {
841+
value: self,
842+
data: len,
843+
f: |&len, self_| {
844+
unsafe {
845+
self_.set_len(len)
846+
}
847+
}
848+
};
849+
for elt in iter.into_iter().take(take) {
850+
ptr::write(ptr, elt);
851+
ptr = ptr.offset(1);
852+
guard.data += 1;
821853
}
822854
}
823855
}

0 commit comments

Comments
 (0)