use std::alloc::{alloc, dealloc, Layout};
use std::marker::{Send, Sync};
use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::aligned::{AlignedAllocator, AlignmentConfig};
use super::arena::{ArenaAllocator, ArenaConfig};
use super::pool::{PoolAllocator, PoolConfig};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocStrategy {
Standard,
Pool,
Arena,
Aligned,
Auto,
}
static GLOBAL_STRATEGY: AtomicUsize = AtomicUsize::new(0);
pub fn set_global_allocator(strategy: AllocStrategy) {
let value = match strategy {
AllocStrategy::Standard => 0,
AllocStrategy::Pool => 1,
AllocStrategy::Arena => 2,
AllocStrategy::Aligned => 3,
AllocStrategy::Auto => 4,
};
GLOBAL_STRATEGY.store(value, Ordering::SeqCst);
}
pub fn get_global_allocator_strategy() -> AllocStrategy {
match GLOBAL_STRATEGY.load(Ordering::SeqCst) {
0 => AllocStrategy::Standard,
1 => AllocStrategy::Pool,
2 => AllocStrategy::Arena,
3 => AllocStrategy::Aligned,
4 => AllocStrategy::Auto,
_ => AllocStrategy::Standard, }
}
pub fn reset_global_allocator() {
GLOBAL_STRATEGY.store(0, Ordering::SeqCst);
}
pub fn get_default_allocator() -> Box<dyn MemoryAllocator> {
match get_global_allocator_strategy() {
AllocStrategy::Standard => Box::new(StandardAllocator),
AllocStrategy::Pool => Box::new(PoolAllocator::new(PoolConfig::default())),
AllocStrategy::Arena => Box::new(ArenaAllocator::new(ArenaConfig::default())),
AllocStrategy::Aligned => Box::new(AlignedAllocator::new(AlignmentConfig::default())),
AllocStrategy::Auto => Box::new(AutoAllocator::new()),
}
}
pub fn recommend_strategy(
alloc_size: usize,
alloc_frequency: AllocFrequency,
simd_usage: bool,
) -> AllocStrategy {
match (alloc_size, alloc_frequency, simd_usage) {
(size, _, _) if size > 1_000_000 => AllocStrategy::Standard,
(size, AllocFrequency::VeryHigh, _) if size < 8192 => AllocStrategy::Pool,
(size, AllocFrequency::High, _) if size < 65536 => AllocStrategy::Arena,
(_, _, true) => AllocStrategy::Aligned,
_ => AllocStrategy::Standard,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocFrequency {
Low,
Medium,
High,
VeryHigh,
}
pub trait MemoryAllocator: Send + Sync {
fn allocate(&self, size: usize) -> Option<NonNull<u8>>;
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
}
#[derive(Debug, Clone, Copy)]
pub struct StandardAllocator;
impl MemoryAllocator for StandardAllocator {
fn allocate(&self, size: usize) -> Option<NonNull<u8>> {
if size == 0 {
return None;
}
let layout = Layout::from_size_align(size, 8).ok()?;
self.allocate_layout(layout)
}
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>> {
unsafe { NonNull::new(alloc(layout)) }
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
dealloc(ptr.as_ptr(), layout);
}
}
pub struct AutoAllocator {
standard: StandardAllocator,
pool: PoolAllocator,
arena: ArenaAllocator,
aligned: AlignedAllocator,
}
impl Default for AutoAllocator {
fn default() -> Self {
Self::new()
}
}
impl AutoAllocator {
pub fn new() -> Self {
Self {
standard: StandardAllocator,
pool: PoolAllocator::new(PoolConfig::default()),
arena: ArenaAllocator::new(ArenaConfig::default()),
aligned: AlignedAllocator::new(AlignmentConfig::default()),
}
}
fn select_allocator(&self, size: usize) -> &dyn MemoryAllocator {
match size {
0..=4096 => &self.pool as &dyn MemoryAllocator,
4097..=65536 => &self.arena as &dyn MemoryAllocator,
_ => &self.standard as &dyn MemoryAllocator,
}
}
}
impl MemoryAllocator for AutoAllocator {
fn allocate(&self, size: usize) -> Option<NonNull<u8>> {
if size == 0 {
return None;
}
if size.is_multiple_of(16) && (16..=65536).contains(&size) {
return self.aligned.allocate(size);
}
self.select_allocator(size).allocate(size)
}
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>> {
if layout.align() >= 16 {
return self.aligned.allocate(layout.size());
}
self.select_allocator(layout.size()).allocate_layout(layout)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
let size = layout.size();
if size.is_multiple_of(16) && (16..=65536).contains(&size) {
self.aligned.deallocate(ptr, size);
return;
}
self.select_allocator(size).deallocate(ptr, layout);
}
}
impl MemoryAllocator for ArenaAllocator {
fn allocate(&self, size: usize) -> Option<NonNull<u8>> {
ArenaAllocator::allocate(self, size)
}
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>> {
self.allocate_aligned(layout.size(), layout.align())
}
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
}
}
impl MemoryAllocator for PoolAllocator {
fn allocate(&self, size: usize) -> Option<NonNull<u8>> {
if size <= self.block_size() {
PoolAllocator::allocate(self)
} else {
None }
}
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>> {
if layout.size() <= self.block_size() {
PoolAllocator::allocate(self)
} else {
None }
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
PoolAllocator::deallocate(self, ptr);
}
}
impl MemoryAllocator for AlignedAllocator {
fn allocate(&self, size: usize) -> Option<NonNull<u8>> {
AlignedAllocator::allocate(self, size)
}
fn allocate_layout(&self, layout: Layout) -> Option<NonNull<u8>> {
self.allocate(layout.size())
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
AlignedAllocator::deallocate(self, ptr, layout.size());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_allocator_settings() {
set_global_allocator(AllocStrategy::Pool);
assert_eq!(get_global_allocator_strategy(), AllocStrategy::Pool);
set_global_allocator(AllocStrategy::Arena);
assert_eq!(get_global_allocator_strategy(), AllocStrategy::Arena);
reset_global_allocator();
assert_eq!(get_global_allocator_strategy(), AllocStrategy::Standard);
}
#[test]
fn test_recommended_strategies() {
let large_alloc_strategy = recommend_strategy(2_000_000, AllocFrequency::Low, false);
assert_eq!(large_alloc_strategy, AllocStrategy::Standard);
let small_freq_strategy = recommend_strategy(100, AllocFrequency::VeryHigh, false);
assert_eq!(small_freq_strategy, AllocStrategy::Pool);
let medium_freq_strategy = recommend_strategy(10_000, AllocFrequency::High, false);
assert_eq!(medium_freq_strategy, AllocStrategy::Arena);
let simd_strategy = recommend_strategy(1024, AllocFrequency::Medium, true);
assert_eq!(simd_strategy, AllocStrategy::Aligned);
}
#[test]
fn test_standard_allocator() {
let allocator = StandardAllocator;
let layout = Layout::from_size_align(100, 8).expect("Layout should succeed");
let ptr = allocator
.allocate_layout(layout)
.expect("Allocation should succeed");
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr.as_ptr(), 100);
for (i, item) in slice.iter_mut().enumerate() {
*item = i as u8;
}
for (i, &item) in slice.iter().enumerate() {
assert_eq!(item, i as u8);
}
allocator.deallocate(ptr, layout);
}
}
#[test]
fn test_auto_allocator() {
let allocator = AutoAllocator::new();
let small_ptr = allocator
.allocate(100)
.expect("Small allocation should succeed");
let medium_ptr = allocator
.allocate(10_000)
.expect("Medium allocation should succeed");
let large_ptr = allocator
.allocate(100_000)
.expect("Large allocation should succeed");
let layout = Layout::from_size_align(64, 64).expect("Layout should succeed");
let aligned_ptr = allocator
.allocate_layout(layout)
.expect("Aligned allocation should succeed");
assert_eq!(
aligned_ptr.as_ptr() as usize % 64,
0,
"Should be 64-byte aligned"
);
unsafe {
allocator.deallocate(
small_ptr,
Layout::from_size_align(100, 8).expect("Layout should succeed"),
);
allocator.deallocate(
medium_ptr,
Layout::from_size_align(10_000, 8).expect("Layout should succeed"),
);
allocator.deallocate(
large_ptr,
Layout::from_size_align(100_000, 8).expect("Layout should succeed"),
);
allocator.deallocate(aligned_ptr, layout);
}
}
#[test]
fn test_get_default_allocator() {
set_global_allocator(AllocStrategy::Standard);
let allocator = get_default_allocator();
let ptr = allocator.allocate(100).expect("Allocation should succeed");
unsafe {
allocator.deallocate(
ptr,
Layout::from_size_align(100, 8).expect("Layout should succeed"),
);
}
set_global_allocator(AllocStrategy::Pool);
let allocator = get_default_allocator();
let ptr = allocator.allocate(100).expect("Allocation should succeed");
unsafe {
allocator.deallocate(
ptr,
Layout::from_size_align(100, 8).expect("Layout should succeed"),
);
}
reset_global_allocator();
}
}