Skip to content

Commit 060d14c

Browse files
Auto merge of #149784 - fereidani:retain_mut, r=<try>
Improve alloc `Vec::retain_mut` performance
2 parents 37aa213 + fa2ae99 commit 060d14c

File tree

1 file changed

+29
-27
lines changed

1 file changed

+29
-27
lines changed

‎library/alloc/src/vec/mod.rs‎

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,8 +2297,7 @@ impl<T, A: Allocator> Vec<T, A> {
22972297
unsafe { self.set_len(0) };
22982298

22992299
// Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked]
2300-
// |<- processed len ->| ^- next to check
2301-
// |<- deleted cnt ->|
2300+
// | ^- write_index ^- read_index
23022301
// |<- original_len ->|
23032302
// Kept: Elements which predicate returns true on.
23042303
// Hole: Moved or dropped element slot.
@@ -2309,31 +2308,31 @@ impl<T, A: Allocator> Vec<T, A> {
23092308
// In cases when predicate and `drop` never panick, it will be optimized out.
23102309
struct BackshiftOnDrop<'a, T, A: Allocator> {
23112310
v: &'a mut Vec<T, A>,
2312-
processed_len: usize,
2313-
deleted_cnt: usize,
2311+
read_index: usize,
2312+
write_index: usize,
23142313
original_len: usize,
23152314
}
23162315

23172316
impl<T, A: Allocator> Drop for BackshiftOnDrop<'_, T, A> {
23182317
fn drop(&mut self) {
2319-
if self.deleted_cnt > 0 {
2318+
if self.write_index < self.read_index {
23202319
// SAFETY: Trailing unchecked items must be valid since we never touch them.
23212320
unsafe {
23222321
ptr::copy(
2323-
self.v.as_ptr().add(self.processed_len),
2324-
self.v.as_mut_ptr().add(self.processed_len - self.deleted_cnt),
2325-
self.original_len - self.processed_len,
2322+
self.v.as_ptr().add(self.read_index),
2323+
self.v.as_mut_ptr().add(self.write_index),
2324+
self.original_len - self.read_index,
23262325
);
23272326
}
23282327
}
23292328
// SAFETY: After filling holes, all items are in contiguous memory.
23302329
unsafe {
2331-
self.v.set_len(self.original_len - self.deleted_cnt);
2330+
self.v.set_len(self.original_len - (self.read_index - self.write_index));
23322331
}
23332332
}
23342333
}
23352334

2336-
let mut g = BackshiftOnDrop { v: self, processed_len: 0, deleted_cnt: 0, original_len };
2335+
let mut g = BackshiftOnDrop { v: self, read_index: 0, write_index: 0, original_len };
23372336

23382337
fn process_loop<F, T, A: Allocator, const DELETED: bool>(
23392338
original_len: usize,
@@ -2342,31 +2341,34 @@ impl<T, A: Allocator> Vec<T, A> {
23422341
) where
23432342
F: FnMut(&mut T) -> bool,
23442343
{
2345-
while g.processed_len != original_len {
2344+
while g.read_index < original_len {
23462345
// SAFETY: Unchecked element must be valid.
2347-
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) };
2346+
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.read_index) };
23482347
if !f(cur) {
2349-
// Advance early to avoid double drop if `drop_in_place` panicked.
2350-
g.processed_len += 1;
2351-
g.deleted_cnt += 1;
2348+
// Advance read_index early to avoid double drop if `drop_in_place` panicked.
2349+
g.read_index += 1;
23522350
// SAFETY: We never touch this element again after dropped.
23532351
unsafe { ptr::drop_in_place(cur) };
23542352
// We already advanced the counter.
2355-
if DELETED {
2356-
continue;
2357-
} else {
2353+
2354+
if !DELETED {
2355+
// We found the first deleted element.
2356+
// Switch to the second stage.
2357+
// read_index is now always greater than write_index.
23582358
break;
23592359
}
2360-
}
2361-
if DELETED {
2362-
// SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element.
2363-
// We use copy for move, and never touch this element again.
2364-
unsafe {
2365-
let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt);
2366-
ptr::copy_nonoverlapping(cur, hole_slot, 1);
2360+
} else {
2361+
if DELETED {
2362+
// SAFETY: `read_index` > `write_index`, so the slots don't overlap.
2363+
// We use copy for move, and never touch the source element again.
2364+
unsafe {
2365+
let hole_slot = g.v.as_mut_ptr().add(g.write_index);
2366+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
2367+
}
23672368
}
2369+
g.write_index += 1;
2370+
g.read_index += 1;
23682371
}
2369-
g.processed_len += 1;
23702372
}
23712373
}
23722374

@@ -2376,7 +2378,7 @@ impl<T, A: Allocator> Vec<T, A> {
23762378
// Stage 2: Some elements were deleted.
23772379
process_loop::<F, T, A, true>(original_len, &mut f, &mut g);
23782380

2379-
// All item are processed. This can be optimized to `set_len` by LLVM.
2381+
// All items are processed. This can be optimized to `set_len` by LLVM.
23802382
drop(g);
23812383
}
23822384

0 commit comments

Comments
 (0)