Skip to content

Commit

Permalink
Auto merge of #91246 - nnethercote:faster-layout-array, r=dtolnay
Browse files Browse the repository at this point in the history
Faster `Layout::array`

`Layout::array` is called (indirectly) by `Vec::push()`, which is typically instantiated many times, and so making it smaller can help with compile times because less LLVM IR is generated.

r? `@ghost`
  • Loading branch information
bors committed Nov 26, 2021
2 parents 6d246f0 + dbfb913 commit ccce985
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 4 deletions.
84 changes: 84 additions & 0 deletions library/alloc/src/raw_vec/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,87 @@ fn reserve_does_not_overallocate() {
assert!(v.capacity() >= 12 + 12 / 2);
}
}

struct ZST;

// A `RawVec` holding zero-sized elements should always look like this.
fn zst_sanity<T>(v: &RawVec<T>) {
assert_eq!(v.capacity(), usize::MAX);
assert_eq!(v.ptr(), core::ptr::Unique::<T>::dangling().as_ptr());
assert_eq!(v.current_memory(), None);
}

#[test]
fn zst() {
let cap_err = Err(crate::collections::TryReserveErrorKind::CapacityOverflow.into());

assert_eq!(std::mem::size_of::<ZST>(), 0);

// All these different ways of creating the RawVec produce the same thing.

let v: RawVec<ZST> = RawVec::new();
zst_sanity(&v);

let v: RawVec<ZST> = RawVec::with_capacity_in(100, Global);
zst_sanity(&v);

let v: RawVec<ZST> = RawVec::with_capacity_in(100, Global);
zst_sanity(&v);

let v: RawVec<ZST> = RawVec::allocate_in(0, AllocInit::Uninitialized, Global);
zst_sanity(&v);

let v: RawVec<ZST> = RawVec::allocate_in(100, AllocInit::Uninitialized, Global);
zst_sanity(&v);

let mut v: RawVec<ZST> = RawVec::allocate_in(usize::MAX, AllocInit::Uninitialized, Global);
zst_sanity(&v);

// Check all these operations work as expected with zero-sized elements.

assert!(!v.needs_to_grow(100, usize::MAX - 100));
assert!(v.needs_to_grow(101, usize::MAX - 100));
zst_sanity(&v);

v.reserve(100, usize::MAX - 100);
//v.reserve(101, usize::MAX - 100); // panics, in `zst_reserve_panic` below
zst_sanity(&v);

v.reserve_exact(100, usize::MAX - 100);
//v.reserve_exact(101, usize::MAX - 100); // panics, in `zst_reserve_exact_panic` below
zst_sanity(&v);

assert_eq!(v.try_reserve(100, usize::MAX - 100), Ok(()));
assert_eq!(v.try_reserve(101, usize::MAX - 100), cap_err);
zst_sanity(&v);

assert_eq!(v.try_reserve_exact(100, usize::MAX - 100), Ok(()));
assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err);
zst_sanity(&v);

assert_eq!(v.grow_amortized(100, usize::MAX - 100), cap_err);
assert_eq!(v.grow_amortized(101, usize::MAX - 100), cap_err);
zst_sanity(&v);

assert_eq!(v.grow_exact(100, usize::MAX - 100), cap_err);
assert_eq!(v.grow_exact(101, usize::MAX - 100), cap_err);
zst_sanity(&v);
}

#[test]
#[should_panic(expected = "capacity overflow")]
fn zst_reserve_panic() {
let mut v: RawVec<ZST> = RawVec::new();
zst_sanity(&v);

v.reserve(101, usize::MAX - 100);
}

#[test]
#[should_panic(expected = "capacity overflow")]
fn zst_reserve_exact_panic() {
let mut v: RawVec<ZST> = RawVec::new();
zst_sanity(&v);

v.reserve_exact(101, usize::MAX - 100);
}
18 changes: 14 additions & 4 deletions library/core/src/alloc/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ impl Layout {
// > `usize::MAX`)
let new_size = self.size() + pad;

Layout::from_size_align(new_size, self.align()).unwrap()
// SAFETY: self.align is already known to be valid and new_size has been
// padded already.
unsafe { Layout::from_size_align_unchecked(new_size, self.align()) }
}

/// Creates a layout describing the record for `n` instances of
Expand Down Expand Up @@ -403,9 +405,17 @@ impl Layout {
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[inline]
pub fn array<T>(n: usize) -> Result<Self, LayoutError> {
let (layout, offset) = Layout::new::<T>().repeat(n)?;
debug_assert_eq!(offset, mem::size_of::<T>());
Ok(layout.pad_to_align())
let array_size = mem::size_of::<T>().checked_mul(n).ok_or(LayoutError)?;

// SAFETY:
// - Size: `array_size` cannot be too big because `size_of::<T>()` must
// be a multiple of `align_of::<T>()`. Therefore, `array_size`
// rounded up to the nearest multiple of `align_of::<T>()` is just
// `array_size`. And `array_size` cannot be too big because it was
// just checked by the `checked_mul()`.
// - Alignment: `align_of::<T>()` will always give an acceptable
// (non-zero, power of two) alignment.
Ok(unsafe { Layout::from_size_align_unchecked(array_size, mem::align_of::<T>()) })
}
}

Expand Down

0 comments on commit ccce985

Please sign in to comment.