Skip to content

Commit 9faa714

Browse files
committed
Auto merge of rust-lang#88075 - Xuanwo:vec_deque_retain, r=dtolnay
Optimize unnecessary check in VecDeque::retain This pr is highly inspired by rust-lang#88060 which shared the same idea: we can split the `for` loop into stages so that we can remove unnecessary checks like `del > 0`. ## Benchmarks Before ```rust test collections::vec_deque::tests::bench_retain_half_10000 ... bench: 290,125 ns/iter (+/- 8,717) test collections::vec_deque::tests::bench_retain_odd_10000 ... bench: 291,588 ns/iter (+/- 9,621) test collections::vec_deque::tests::bench_retain_whole_10000 ... bench: 287,426 ns/iter (+/- 9,009) ``` After ```rust test collections::vec_deque::tests::bench_retain_half_10000 ... bench: 243,940 ns/iter (+/- 8,563) test collections::vec_deque::tests::bench_retain_odd_10000 ... bench: 242,768 ns/iter (+/- 3,903) test collections::vec_deque::tests::bench_retain_whole_10000 ... bench: 202,926 ns/iter (+/- 6,332) ``` Based on the current benchmark, this PR will improve the perf of `VecDeque::retain` by around 16%. For special cases, the improvement will be up to 30%. Signed-off-by: Xuanwo <[email protected]>
2 parents d3e2578 + e32f4c0 commit 9faa714

File tree

2 files changed

+57
-8
lines changed

2 files changed

+57
-8
lines changed

library/alloc/src/collections/vec_deque/mod.rs

+24-8
Original file line numberDiff line numberDiff line change
@@ -2129,16 +2129,32 @@ impl<T, A: Allocator> VecDeque<T, A> {
21292129
F: FnMut(&T) -> bool,
21302130
{
21312131
let len = self.len();
2132-
let mut del = 0;
2133-
for i in 0..len {
2134-
if !f(&self[i]) {
2135-
del += 1;
2136-
} else if del > 0 {
2137-
self.swap(i - del, i);
2132+
let mut idx = 0;
2133+
let mut cur = 0;
2134+
2135+
// Stage 1: All values are retained.
2136+
while cur < len {
2137+
if !f(&self[cur]) {
2138+
cur += 1;
2139+
break;
21382140
}
2141+
cur += 1;
2142+
idx += 1;
21392143
}
2140-
if del > 0 {
2141-
self.truncate(len - del);
2144+
// Stage 2: Swap retained value into current idx.
2145+
while cur < len {
2146+
if !f(&self[cur]) {
2147+
cur += 1;
2148+
continue;
2149+
}
2150+
2151+
self.swap(idx, cur);
2152+
cur += 1;
2153+
idx += 1;
2154+
}
2155+
// Stage 3: Trancate all values after idx.
2156+
if cur != idx {
2157+
self.truncate(idx);
21422158
}
21432159
}
21442160

library/alloc/src/collections/vec_deque/tests.rs

+33
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,39 @@ fn bench_pop_back_100(b: &mut test::Bencher) {
4040
})
4141
}
4242

43+
#[bench]
44+
#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
45+
fn bench_retain_whole_10000(b: &mut test::Bencher) {
46+
let v = (1..100000).collect::<VecDeque<u32>>();
47+
48+
b.iter(|| {
49+
let mut v = v.clone();
50+
v.retain(|x| *x > 0)
51+
})
52+
}
53+
54+
#[bench]
55+
#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
56+
fn bench_retain_odd_10000(b: &mut test::Bencher) {
57+
let v = (1..100000).collect::<VecDeque<u32>>();
58+
59+
b.iter(|| {
60+
let mut v = v.clone();
61+
v.retain(|x| x & 1 == 0)
62+
})
63+
}
64+
65+
#[bench]
66+
#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
67+
fn bench_retain_half_10000(b: &mut test::Bencher) {
68+
let v = (1..100000).collect::<VecDeque<u32>>();
69+
70+
b.iter(|| {
71+
let mut v = v.clone();
72+
v.retain(|x| *x > 50000)
73+
})
74+
}
75+
4376
#[bench]
4477
#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
4578
fn bench_pop_front_100(b: &mut test::Bencher) {

0 commit comments

Comments
 (0)