mnemosyne-memory-core 0.3.0

Core allocator types, policies, and validation for Mnemosyne
Documentation
//! Layout constants for the Mnemosyne memory allocator.

/// The size of a segment (2MB).
pub const SEGMENT_SIZE: usize = 2 * 1024 * 1024;

/// The alignment of a segment (2MB).
pub const SEGMENT_ALIGN: usize = SEGMENT_SIZE;

/// The size of a page (64KB).
pub const PAGE_SIZE: usize = 64 * 1024;

/// The shift amount corresponding to PAGE_SIZE (e.g. 16 for 64KB).
pub const PAGE_SHIFT: usize = PAGE_SIZE.trailing_zeros() as usize;

/// The alignment of a page (64KB).
pub const PAGE_ALIGN: usize = PAGE_SIZE;

/// The number of pages per segment (32).
pub const PAGES_PER_SEGMENT: usize = SEGMENT_SIZE / PAGE_SIZE;

/// The maximum size of a small allocation class (16KB).
///
/// Raised from 8KB (MN-461): a request above this ceiling takes the
/// large/huge path, which maps `size + SEGMENT_ALIGN + PAGE_SIZE` per
/// allocation, so every 8-16KB object reserved its own ~2MB segment and
/// paid 41 ns against 2.6 ns on the class path. Four classes (10240,
/// 12288, 14336, 16384) now pack four to twelve blocks per 64KB page.
pub const MAX_SMALL_ALLOC_SIZE: usize = 16 * 1024;

/// The smallest size-class block, in bytes.
///
/// This is the allocation fast-path alignment ceiling: requests whose
/// alignment is no larger than this value can be served from small pages.
pub const MIN_BLOCK_SIZE: usize = 16;

/// Maximum single allocation payload size accepted by public allocation entry points.
///
/// This mirrors Rust `Layout`'s pointer-offset safety bound: allocated object
/// sizes must not exceed `isize::MAX`.
pub const MAX_ALLOC_SIZE: usize = isize::MAX as usize;

/// The total number of small size classes.
///
/// Raised from 48 to 52 (MN-REF-1): the 8–16 KB range is now subdivided
/// into 1024-byte steps instead of 2048-byte steps, reducing worst-case
/// internal fragmentation from 25% to 12.5% for scientific workloads that
/// allocate in the 8–16 KB band (FFT buffers, simulation cells, etc.).
pub const NUM_SIZE_CLASSES: usize = 52;

/// The maximum number of segments retained in the global pool (compile-time limit).
pub const MAX_RETAINED_SEGMENTS_LIMIT: usize = 1024;

// Compile-time structural invariant checks.
//
// These `const _: () = assert!(...)` items are evaluated by the compiler
// before any code is generated, so any constant drift that breaks an
// allocator-wide layout assumption produces a hard build failure rather
// than a silent runtime fault. They cost zero bytes and zero instructions.

/// `SEGMENT_SIZE` and `PAGE_SIZE` must be powers of two so the bitmask-based
/// address rounding (`addr & !(SEGMENT_SIZE - 1)`, `addr & !(PAGE_SIZE - 1)`)
/// produces the correct base address.
const _: () = assert!(
    SEGMENT_SIZE.is_power_of_two(),
    "SEGMENT_SIZE must be a power of two for bitmask address rounding"
);
const _: () = assert!(
    PAGE_SIZE.is_power_of_two(),
    "PAGE_SIZE must be a power of two for bitmask address rounding"
);

/// `SEGMENT_ALIGN` is referenced as both an alignment cap (rejecting larger
/// alignments in `is_valid_alloc_request`) and as the rounding modulus for
/// page-to-segment recovery. The two roles must agree on the same value.
const _: () = assert!(
    SEGMENT_ALIGN == SEGMENT_SIZE,
    "SEGMENT_ALIGN must equal SEGMENT_SIZE so the small-free classifier can recover the segment header by rounding"
);
const _: () = assert!(
    PAGE_ALIGN == PAGE_SIZE,
    "PAGE_ALIGN must equal PAGE_SIZE so page-base derivation matches the page array stride"
);

/// `PAGES_PER_SEGMENT * PAGE_SIZE == SEGMENT_SIZE` is the array-stride
/// derivation used by `Segment::initialize` and the small-free classifier.
const _: () = assert!(
    PAGES_PER_SEGMENT * PAGE_SIZE == SEGMENT_SIZE,
    "PAGES_PER_SEGMENT must tile SEGMENT_SIZE exactly with PAGE_SIZE strides"
);
const _: () = assert!(
    PAGES_PER_SEGMENT <= (u8::MAX as usize) + 1,
    "Page::page_index stores segment-local page indices in u8"
);

/// At least one page must be available for small allocations after Page 0
/// is reserved for segment metadata.
const _: () = assert!(
    PAGES_PER_SEGMENT >= 2,
    "PAGES_PER_SEGMENT must reserve Page 0 for metadata and leave at least one slicing page"
);

/// Every small allocation must fit inside a single page so `Page::initialize_free_list`
/// can lay out at least one block of every size class.
const _: () = assert!(
    MAX_SMALL_ALLOC_SIZE <= PAGE_SIZE,
    "MAX_SMALL_ALLOC_SIZE must fit inside one PAGE_SIZE"
);

/// `MAX_ALLOC_SIZE` is the public payload bound; it must be at least one
/// segment so `allocate_large_or_huge` can serve any request the
/// small-allocation path rejects.
const _: () = assert!(
    MAX_ALLOC_SIZE >= SEGMENT_SIZE,
    "MAX_ALLOC_SIZE must accept at least one segment-sized payload"
);

/// `NUM_SIZE_CLASSES` is the array dimension for per-class metadata
/// (`active_pages`, `full_pages`, `size_class_occupancy`); it must be
/// non-zero for the arrays to hold any state.
const _: () = assert!(
    NUM_SIZE_CLASSES > 0,
    "NUM_SIZE_CLASSES must be non-zero so per-class allocator arrays hold at least one entry"
);
const _: () = assert!(
    NUM_SIZE_CLASSES <= (u8::MAX as usize) + 1,
    "Page::size_class stores size-class indices in u8"
);

/// `MIN_BLOCK_SIZE` must divide `PAGE_SIZE` exactly so the densest page is
/// fully tiled with no trailing partial block, keeping the block-count
/// derivation above exact.
const _: () = assert!(
    PAGE_SIZE.is_multiple_of(MIN_BLOCK_SIZE),
    "MIN_BLOCK_SIZE must divide PAGE_SIZE exactly"
);

/// `MIN_BLOCK_SIZE` must be a power of two for alignment arithmetic.
const _: () = assert!(
    MIN_BLOCK_SIZE.is_power_of_two(),
    "MIN_BLOCK_SIZE must be a power of two for alignment checks"
);

/// `MAX_SMALL_ALLOC_SIZE` must be a power of two to simplify Lemire
/// reciprocal table bounds and keep the size-class table regular.
const _: () = assert!(
    MAX_SMALL_ALLOC_SIZE.is_power_of_two(),
    "MAX_SMALL_ALLOC_SIZE must be a power of two"
);