1
Fork 0

Auto merge of #87777 - the8472:fix-mir-max-rss, r=oli-obk,joshtriplett

Use zeroed allocations in the mir interpreter instead eagerly touching the memory

#86255 introduced a 30% regression in [page faults](https://perf.rust-lang.org/compare.html?start=64ae15ddd3f3cca7036ab2b2f3a6b130b62af4da&end=39e20f1ae5f13451eb35247808d6a2527cb7d060&stat=faults
) and a 3% regression in [max-rss](https://perf.rust-lang.org/index.html?start=2021-07-01&end=&absolute=false&stat=max-rss) in the ctfe-stress benchmarks.
That's most likely happened because it separated allocation from initialization of the vec which defeats the zero-optimization.

Currently there's no allocation API that is fallible, zeroing and returns a slice, so this PR introduces one and then uses that to solve the problem. In principle `vec.resize(len, 0)` could be optimized to use `alloc::grow_zeroed` where appropriate but that would require new specializations and new plumbing in `RawVec`.
This commit is contained in:
bors 2021-08-06 12:11:30 +00:00
commit 4c29cc8fd0
3 changed files with 72 additions and 6 deletions

View file

@ -23,6 +23,7 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(allocator_api)]
#![feature(array_windows)]
#![feature(assert_matches)]
#![feature(backtrace)]
@ -33,6 +34,7 @@
#![feature(discriminant_kind)]
#![feature(never_type)]
#![feature(extern_types)]
#![feature(new_uninit)]
#![feature(nll)]
#![feature(once_cell)]
#![feature(min_specialization)]

View file

@ -28,7 +28,7 @@ use crate::ty;
pub struct Allocation<Tag = AllocId, Extra = ()> {
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer.
bytes: Vec<u8>,
bytes: Box<[u8]>,
/// Maps from byte addresses to extra data for each pointer.
/// Only the first byte of a pointer is inserted into the map; i.e.,
/// every entry in this map applies to `pointer_size` consecutive bytes starting
@ -112,7 +112,7 @@ impl<Tag> Allocation<Tag> {
align: Align,
mutability: Mutability,
) -> Self {
let bytes = slice.into().into_owned();
let bytes = Box::<[u8]>::from(slice.into());
let size = Size::from_bytes(bytes.len());
Self {
bytes,
@ -131,8 +131,7 @@ impl<Tag> Allocation<Tag> {
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
/// available to the compiler to do so.
pub fn uninit(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'static, Self> {
let mut bytes = Vec::new();
bytes.try_reserve(size.bytes_usize()).map_err(|_| {
let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| {
// This results in an error that can happen non-deterministically, since the memory
// available to the compiler can change between runs. Normally queries are always
// deterministic. However, we can be non-determinstic here because all uses of const
@ -146,7 +145,8 @@ impl<Tag> Allocation<Tag> {
});
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
})?;
bytes.resize(size.bytes_usize(), 0);
// SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]>
let bytes = unsafe { bytes.assume_init() };
Ok(Allocation {
bytes,
relocations: Relocations::new(),