use crate::memory_management::Cleanup;
use crate::{
memory_management::{
ErrorGraph, ManagedMemoryHandle, MemoryPoolReport,
memory_pool::{ExclusiveLayout, ExclusiveMemoryPool, MemoryPool, PageMapping},
},
server::IoError,
storage::ComputeStorage,
};
use alloc::vec::Vec;
use cubecl_environment::backtrace::BackTrace;
use cubecl_ir::MemoryDeviceProperties;
const MIN_BUCKET_SIZE: u64 = 1024 * 32;
const MAX_BUCKETS: usize = 24;
const BASE_DEALLOC_PERIOD: u64 = 5000;
const DEALLOC_SCALE: u64 = 1024 * 1024 * 1024;
pub struct ExclusivePools {
buckets: Vec<ExclusiveMemoryPool>,
}
impl ExclusivePools {
pub fn new(properties: &MemoryDeviceProperties) -> Self {
let buckets = bucket_sizes(properties.max_page_size, properties.alignment)
.into_iter()
.enumerate()
.map(|(index, size)| {
let dealloc_period = (BASE_DEALLOC_PERIOD as f64
* (1.0 + size as f64 / DEALLOC_SCALE as f64).round())
as u64;
ExclusiveMemoryPool::new(ExclusiveLayout {
max_alloc_size: size,
alignment: properties.alignment,
dealloc_period,
pool: index as u8,
})
})
.collect();
Self { buckets }
}
pub fn pool(&self, index: u8) -> Option<&ExclusiveMemoryPool> {
self.buckets.get(index as usize)
}
pub fn pool_mut(&mut self, index: u8) -> Option<&mut ExclusiveMemoryPool> {
self.buckets.get_mut(index as usize)
}
pub fn reserve<Storage: ComputeStorage>(
&mut self,
storage: &mut Storage,
size: u64,
mapping: PageMapping,
failures: &mut ErrorGraph,
) -> Result<ManagedMemoryHandle, IoError> {
if let Some(handle) = self.try_reserve(size, failures) {
return Ok(handle);
}
match self.buckets.iter_mut().find(|pool| pool.accept(size)) {
Some(pool) => pool.alloc(storage, size, mapping, failures),
None => Err(IoError::BufferTooBig {
size,
backtrace: BackTrace::capture(),
}),
}
}
pub fn try_reserve(
&mut self,
size: u64,
failures: &mut ErrorGraph,
) -> Option<ManagedMemoryHandle> {
self.buckets
.iter_mut()
.filter(|pool| pool.accept(size))
.find_map(|pool| pool.try_reserve(size, failures))
}
pub fn cleanup<Storage: ComputeStorage>(
&mut self,
storage: &mut Storage,
alloc_nr: u64,
cleanup: Cleanup,
failures: &mut ErrorGraph,
) {
for pool in self.buckets.iter_mut() {
pool.cleanup(storage, alloc_nr, cleanup, failures);
}
}
pub fn report(&self) -> Vec<MemoryPoolReport> {
self.buckets
.iter()
.map(ExclusiveMemoryPool::report)
.collect()
}
}
impl core::fmt::Display for ExclusivePools {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for pool in self.buckets.iter() {
write!(f, "{pool}")?;
}
Ok(())
}
}
fn bucket_sizes(max_size: u64, alignment: u64) -> Vec<u64> {
let log_min = (MIN_BUCKET_SIZE as f64).ln();
let log_range = (max_size as f64).ln() - log_min;
let mut sizes: Vec<u64> = (0..MAX_BUCKETS)
.map(|index| {
let position = index as f64 / (MAX_BUCKETS - 1) as f64;
let size = (log_min + log_range * position).exp() as u64;
size.next_multiple_of(alignment)
})
.collect();
sizes.dedup();
sizes
}