Skip to content

Commit 67180ef

Browse files
committed
Optimize vec::retain performance
This simply moves the loops into the inner function which leads to better results. ``` old: test vec::bench_retain_100000 ... bench: 203,828 ns/iter (+/- 2,101) test vec::bench_retain_iter_100000 ... bench: 63,324 ns/iter (+/- 12,305) test vec::bench_retain_whole_100000 ... bench: 42,989 ns/iter (+/- 291) new: test vec::bench_retain_100000 ... bench: 42,180 ns/iter (+/- 451) test vec::bench_retain_iter_100000 ... bench: 65,167 ns/iter (+/- 11,971) test vec::bench_retain_whole_100000 ... bench: 33,736 ns/iter (+/- 12,404) ```
1 parent d0f38cc commit 67180ef

File tree

1 file changed

+29
-32
lines changed

1 file changed

+29
-32
lines changed

Diff for: library/alloc/src/vec/mod.rs

+29-32
Original file line numberDiff line numberDiff line change
@@ -1520,49 +1520,46 @@ impl<T, A: Allocator> Vec<T, A> {
15201520

15211521
let mut g = BackshiftOnDrop { v: self, processed_len: 0, deleted_cnt: 0, original_len };
15221522

1523-
// process_one return a bool indicates whether the processing element should be retained.
1524-
#[inline(always)]
1525-
fn process_one<F, T, A: Allocator, const DELETED: bool>(
1523+
fn process_loop<F, T, A: Allocator, const DELETED: bool>(
1524+
original_len: usize,
15261525
f: &mut F,
15271526
g: &mut BackshiftOnDrop<'_, T, A>,
1528-
) -> bool
1529-
where
1527+
) where
15301528
F: FnMut(&mut T) -> bool,
15311529
{
1532-
// SAFETY: Unchecked element must be valid.
1533-
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) };
1534-
if !f(cur) {
1535-
// Advance early to avoid double drop if `drop_in_place` panicked.
1536-
g.processed_len += 1;
1537-
g.deleted_cnt += 1;
1538-
// SAFETY: We never touch this element again after dropped.
1539-
unsafe { ptr::drop_in_place(cur) };
1540-
// We already advanced the counter.
1541-
return false;
1542-
}
1543-
if DELETED {
1544-
// SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element.
1545-
// We use copy for move, and never touch this element again.
1546-
unsafe {
1547-
let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt);
1548-
ptr::copy_nonoverlapping(cur, hole_slot, 1);
1530+
while g.processed_len != original_len {
1531+
// SAFETY: Unchecked element must be valid.
1532+
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) };
1533+
if !f(cur) {
1534+
// Advance early to avoid double drop if `drop_in_place` panicked.
1535+
g.processed_len += 1;
1536+
g.deleted_cnt += 1;
1537+
// SAFETY: We never touch this element again after dropped.
1538+
unsafe { ptr::drop_in_place(cur) };
1539+
// We already advanced the counter.
1540+
if DELETED {
1541+
continue;
1542+
} else {
1543+
break;
1544+
}
1545+
}
1546+
if DELETED {
1547+
// SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element.
1548+
// We use copy for move, and never touch this element again.
1549+
unsafe {
1550+
let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt);
1551+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
1552+
}
15491553
}
1554+
g.processed_len += 1;
15501555
}
1551-
g.processed_len += 1;
1552-
return true;
15531556
}
15541557

15551558
// Stage 1: Nothing was deleted.
1556-
while g.processed_len != original_len {
1557-
if !process_one::<F, T, A, false>(&mut f, &mut g) {
1558-
break;
1559-
}
1560-
}
1559+
process_loop::<F, T, A, false>(original_len, &mut f, &mut g);
15611560

15621561
// Stage 2: Some elements were deleted.
1563-
while g.processed_len != original_len {
1564-
process_one::<F, T, A, true>(&mut f, &mut g);
1565-
}
1562+
process_loop::<F, T, A, true>(original_len, &mut f, &mut g);
15661563

15671564
// All item are processed. This can be optimized to `set_len` by LLVM.
15681565
drop(g);

0 commit comments

Comments
 (0)