mmtk/util/rust_util/zeroed_alloc.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
//! This module is for allocating large arrays or vectors with initial zero values.
//!
//! Currently we use the [`Zeroable`] trait from the `bytemuck` crate to label types that are safe
//! for zero initialization. If one day Rust provides a standard way to optimize for zeroed
//! allocation of vectors of composite types, we can switch to the standard mechanism.
//!
//! Note: The standard library uses the `IsZero` trait to specialize the intialization of `Vec<T>`
//! if the initial element values are zero. Primitive type, such as `i8`, `usize`, `f32`, as well
//! as types with known representations such as `Option<NonZeroUsize>` implement the `IsZero` trait.
//! However, it has several limitations.
//!
//! 1. Composite types, such as `SpaceDescriptor(usize)`, don't implement the `IsZero` trait, even
//! if they have the `#[repr(transparent)]` annotation.
//! 2. The `IsZero` trait is private to the `std` module, and we cannot use it.
//!
//! Therefore, `vec![0usize; 33554432]` takes only 4 **microseconds**, while
//! `vec![SpaceDescriptor(0); 33554432]` will take 22 **milliseconds** to execute on some machine.
//! If such an allocation happens during start-up, the delay will be noticeable to light-weight
//! scripting languages, such as Ruby.
//!
//! The [`new_zeroed_vec`] function in this module can allocate zeroed vectors as fast as `vec![0;
//! LEN]`;
use std::alloc::{alloc_zeroed, handle_alloc_error, Layout};
use bytemuck::Zeroable;
/// Allocate a `Vec<T>` of all-zero values. `T` must implement [`bytemuck::Zeroable`].
///
/// This intends to be a faster alternative to `vec![T(0), size]`. It will allocate pre-zeroed
/// buffer, and not store zero values to its elements as part of initialization.
///
/// It is useful when creating large (hundreds of megabytes) Vecs when the execution time is
/// critical (such as during start-up, where a 100ms delay is obvious to small applications.)
///
/// Arguments:
///
/// - `T`: The element type.
/// - `size`: The length and capacity of the created vector.
///
/// Returns the created vector.
pub(crate) fn new_zeroed_vec<T: Zeroable>(size: usize) -> Vec<T> {
let layout = Layout::array::<T>(size).unwrap();
let ptr = unsafe { alloc_zeroed(layout) } as *mut T;
if ptr.is_null() {
handle_alloc_error(layout);
}
unsafe { Vec::from_raw_parts(ptr, size, size) }
}