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