diff --git a/oscars/src/alloc/mempool3/mod.rs b/oscars/src/alloc/mempool3/mod.rs index 90a8f23..286c12c 100644 --- a/oscars/src/alloc/mempool3/mod.rs +++ b/oscars/src/alloc/mempool3/mod.rs @@ -28,19 +28,21 @@ impl From for PoolAllocError { } } -const SIZE_CLASSES: &[usize] = &[16, 24, 32, 48, 64, 96, 128, 192, 256, 512, 1024, 2048]; +const SIZE_CLASSES: &[usize] = &[ + 16, 24, 32, 48, 64, 96, 128, 192, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, +]; #[inline(always)] fn size_class_index_for(size: usize) -> usize { // binary search over size classes let idx = SIZE_CLASSES.partition_point(|&sc| sc < size); - debug_assert!( + assert!( idx < SIZE_CLASSES.len(), "object size {size}B exceeds the largest size class ({}B); \ consider adding a larger class", SIZE_CLASSES.last().unwrap() ); - idx.min(SIZE_CLASSES.len() - 1) + idx } const DEFAULT_PAGE_SIZE: usize = 262_144; @@ -58,7 +60,7 @@ pub struct PoolAllocator<'alloc> { // cached index of the last pool used by free_slot pub(crate) free_cache: Cell, // per size class cached index of the last pool used by alloc_slot - pub(crate) alloc_cache: [Cell; 12], + pub(crate) alloc_cache: [Cell; 17], // empty slot pools kept alive to avoid OS reallocation on the next cycle pub(crate) recycled_pools: Vec, // maximum number of idle pages held across all size classes @@ -78,20 +80,7 @@ impl<'alloc> Default for PoolAllocator<'alloc> { slot_pools: Vec::new(), bump_pages: Vec::new(), free_cache: Cell::new(usize::MAX), - alloc_cache: [ - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - ], + alloc_cache: core::array::from_fn(|_| Cell::new(usize::MAX)), recycled_pools: Vec::new(), // keep two empty pages per size class to reduce OS overhead max_recycled: SIZE_CLASSES.len() * 2, diff --git a/oscars/src/alloc/mempool3/tests.rs b/oscars/src/alloc/mempool3/tests.rs index 1991dcc..2855fe2 100644 --- a/oscars/src/alloc/mempool3/tests.rs +++ b/oscars/src/alloc/mempool3/tests.rs @@ -234,3 +234,33 @@ fn max_recycled_cap_respected() { assert_eq!(allocator.recycled_pools.len(), 1); assert!(allocator.current_heap_size < heap_before); } + +#[test] +fn alloc_large_object_success() { + let mut allocator = PoolAllocator::default().with_page_size(16384); + // allocate a size that maps to a class > 2048 + // e.g. 4096 size class => size 3000 + struct LargeObject { + _data: [u8; 3000], + } + let a = allocator + .try_alloc(LargeObject { _data: [0; 3000] }) + .unwrap(); + assert_eq!(allocator.pools_len(), 1); + + // Test that the caching works for the new size classes + let _b = allocator + .try_alloc(LargeObject { _data: [0; 3000] }) + .unwrap(); + assert_eq!(allocator.pools_len(), 1); // should still fit in the same page +} + +#[test] +#[should_panic(expected = "object size")] +fn alloc_oversized_object_panics() { + let mut allocator = PoolAllocator::default().with_page_size(128 * 1024); + struct OversizedObject { + _data: [u8; 70000], + } + let _ = allocator.try_alloc(OversizedObject { _data: [0; 70000] }); +}