Skip to content

Commit eafa7ae

Browse files
committed
perf(allocator/vec2): align min amortized cap size with std
1 parent 9d1db26 commit eafa7ae

File tree

1 file changed

+27
-19
lines changed

1 file changed

+27
-19
lines changed

crates/oxc_allocator/src/vec2/raw_vec.rs

+27-19
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,24 @@ pub struct RawVec<'a, T> {
6969
a: &'a Bump,
7070
}
7171

72+
// Tiny Vecs are dumb. Skip to:
73+
// - 8 if the element size is 1, because any heap allocators is likely
74+
// to round up a request of less than 8 bytes to at least 8 bytes.
75+
// - 4 if elements are moderate-sized (<= 1 KiB).
76+
// - 1 otherwise, to avoid wasting too much space for very short Vecs.
77+
const fn min_non_zero_cap(size: usize) -> usize {
78+
if size == 1 {
79+
8
80+
} else if size <= 1024 {
81+
4
82+
} else {
83+
1
84+
}
85+
}
86+
7287
impl<'a, T> RawVec<'a, T> {
88+
pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::<T>());
89+
7390
/// Like `new` but parameterized over the choice of allocator for
7491
/// the returned RawVec.
7592
pub fn new_in(a: &'a Bump) -> Self {
@@ -351,22 +368,6 @@ impl<'a, T> RawVec<'a, T> {
351368
}
352369
}
353370

354-
/// Calculates the buffer's new size given that it'll hold `len +
355-
/// additional` elements. This logic is used in amortized reserve methods.
356-
/// Returns `(new_capacity, new_alloc_size)`.
357-
fn amortized_new_size(
358-
&self,
359-
len: usize,
360-
additional: usize,
361-
) -> Result<usize, CollectionAllocErr> {
362-
// Nothing we can really do about these checks :(
363-
let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
364-
// Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
365-
let double_cap = self.cap * 2;
366-
// `double_cap` guarantees exponential growth.
367-
Ok(cmp::max(double_cap, required_cap))
368-
}
369-
370371
/// The same as `reserve`, but returns on errors instead of panicking or aborting.
371372
pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), CollectionAllocErr> {
372373
if self.needs_to_grow(len, additional) {
@@ -620,12 +621,19 @@ impl<'a, T> RawVec<'a, T> {
620621
// If we make it past the first branch then we are guaranteed to
621622
// panic.
622623

623-
let new_cap = self.amortized_new_size(len, additional)?;
624-
let new_layout = Layout::array::<T>(new_cap).map_err(|_| CapacityOverflow)?;
624+
// Nothing we can really do about these checks, sadly.
625+
let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
626+
627+
// This guarantees exponential growth. The doubling cannot overflow
628+
// because `cap <= isize::MAX` and the type of `cap` is `usize`.
629+
let cap = cmp::max(self.cap() * 2, required_cap);
630+
let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
631+
632+
let new_layout = Layout::array::<T>(cap).map_err(|_| CapacityOverflow)?;
625633

626634
self.ptr = self.finish_grow(new_layout)?.cast();
627635

628-
self.cap = new_cap;
636+
self.cap = cap;
629637

630638
Ok(())
631639
}

0 commit comments

Comments
 (0)