Skip to content

Commit 5df02fd

Browse files
authored
Merge pull request #74 from bluss/improved-extend
Improve .extend() performance
2 parents d89699f + 793ad30 commit 5df02fd

File tree

3 files changed

+83
-3
lines changed

3 files changed

+83
-3
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ version = "1.0"
3030

3131
[dev-dependencies]
3232
matches = { version = "0.1" }
33+
bencher = "0.1.4"
34+
35+
[[bench]]
36+
name = "extend"
37+
harness = false
3338

3439
[features]
3540
default = ["std"]

benches/extend.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
extern crate arrayvec;
3+
#[macro_use] extern crate bencher;
4+
5+
use arrayvec::ArrayVec;
6+
7+
use bencher::Bencher;
8+
9+
fn extend_with_constant(b: &mut Bencher) {
10+
let mut v = ArrayVec::<[u8; 512]>::new();
11+
let cap = v.capacity();
12+
b.iter(|| {
13+
v.clear();
14+
v.extend((0..cap).map(|_| 1));
15+
v[0]
16+
});
17+
b.bytes = v.capacity() as u64;
18+
}
19+
20+
fn extend_with_range(b: &mut Bencher) {
21+
let mut v = ArrayVec::<[u8; 512]>::new();
22+
let cap = v.capacity();
23+
b.iter(|| {
24+
v.clear();
25+
v.extend((0..cap).map(|x| x as _));
26+
v[0]
27+
});
28+
b.bytes = v.capacity() as u64;
29+
}
30+
31+
fn extend_with_slice(b: &mut Bencher) {
32+
let mut v = ArrayVec::<[u8; 512]>::new();
33+
let data = [1; 512];
34+
b.iter(|| {
35+
v.clear();
36+
v.extend(data.iter().cloned());
37+
v[0]
38+
});
39+
b.bytes = v.capacity() as u64;
40+
}
41+
42+
benchmark_group!(benches, extend_with_constant, extend_with_range, extend_with_slice);
43+
benchmark_main!(benches);

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)