Skip to content

Commit c325291

Browse files
committed
perf(allocator/vec): Vec::extend_desugared set len only at end
1 parent 99ad11f commit c325291

File tree

1 file changed

+22
-8
lines changed
  • crates/oxc_allocator/src/vec2

1 file changed

+22
-8
lines changed

crates/oxc_allocator/src/vec2/mod.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,30 +2298,44 @@ impl<'bump, T: 'bump> Vec<'bump, T> {
22982298
// for item in iterator {
22992299
// self.push(item);
23002300
// }
2301+
let mut len = self.len_usize();
23012302
while let Some(element) = iterator.next() {
2302-
let len = self.len_usize();
23032303
if len == self.capacity_usize() {
23042304
// This reallocation path is rarely taken, especially with prior reservation,
23052305
// so mark it `#[cold]` and `#[inline(never)]` helps the compiler optimize the
23062306
// common case, and prevents this cold path from being inlined to the `while` loop,
23072307
// which increases the execution instructions and hits the performance.
23082308
#[cold]
23092309
#[inline(never)]
2310-
fn reserve_slow<T>(v: &mut Vec<T>, iterator: &impl Iterator) {
2310+
fn reserve_slow<T>(v: &mut Vec<T>, iterator: &impl Iterator, len: usize) {
2311+
// `len` is always `<= u32::MAX`. If attempted to grow the `Vec` beyond this
2312+
// on a previous turn, `RawVec::reserve` would have panicked.
2313+
#[expect(clippy::cast_possible_truncation)]
2314+
let len = len as u32;
2315+
23112316
let (lower, _) = iterator.size_hint();
2312-
v.reserve(lower.saturating_add(1));
2317+
v.buf.reserve(len, lower.saturating_add(1));
23132318
}
23142319

2315-
reserve_slow(self, &iterator);
2320+
reserve_slow(self, &iterator, len);
23162321
}
23172322
unsafe {
23182323
ptr::write(self.as_mut_ptr().add(len), element);
2319-
// Since next() executes user code which can panic we have to bump the length
2320-
// after each step.
2321-
// NB can't overflow since we would have had to alloc the address space
2322-
self.set_len(len + 1);
2324+
// Can't overflow since we would have had to alloc the address space
2325+
len += 1;
23232326
}
23242327
}
2328+
2329+
// Update `len` to reflect the extra elements we've pushed.
2330+
//
2331+
// std library version of this method updates `len` after writing each element,
2332+
// because `iterator.next()` could panic.
2333+
// If that happens, `len` needs to contain all the elements written so far,
2334+
// so they get dropped when the `Vec` is dropped.
2335+
// But our `Vec` requires that `T` is not `Drop`, so we don't need to worry about that.
2336+
//
2337+
// SAFETY: `len` cannot be `> u32::MAX` because `reserve` would have already panicked if it was.
2338+
unsafe { self.set_len(len) };
23252339
}
23262340
}
23272341

0 commit comments

Comments
 (0)