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% ```
This commit is contained in:
+35
-3
@@ -805,6 +805,21 @@ impl<'a, A: Array> Drop for Drain<'a, A>
|
||||
}
|
||||
}
|
||||
|
||||
struct ScopeExitGuard<T, Data, F>
|
||||
where F: FnMut(&Data, &mut T)
|
||||
{
|
||||
value: T,
|
||||
data: Data,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<T, Data, F> Drop for ScopeExitGuard<T, Data, F>
|
||||
where F: FnMut(&Data, &mut T)
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
(self.f)(&self.data, &mut self.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -815,9 +830,26 @@ impl<'a, A: Array> Drop for Drain<'a, A>
|
||||
impl<A: Array> Extend<A::Item> for ArrayVec<A> {
|
||||
fn extend<T: IntoIterator<Item=A::Item>>(&mut self, iter: T) {
|
||||
let take = self.capacity() - self.len();
|
||||
for elt in iter.into_iter().take(take) {
|
||||
unsafe {
|
||||
self.push_unchecked(elt);
|
||||
unsafe {
|
||||
let len = self.len();
|
||||
let mut ptr = self.as_mut_ptr().offset(len as isize);
|
||||
// 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.
|
||||
let mut guard = ScopeExitGuard {
|
||||
value: self,
|
||||
data: len,
|
||||
f: |&len, self_| {
|
||||
unsafe {
|
||||
self_.set_len(len)
|
||||
}
|
||||
}
|
||||
};
|
||||
for elt in iter.into_iter().take(take) {
|
||||
ptr::write(ptr, elt);
|
||||
ptr = ptr.offset(1);
|
||||
guard.data += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user