use super::*;
use crate::legacy_growth::{fail, Binding};
use crate::{GrowthBucket, GrowthQuota, LegacyGrowthSnapshot};
pub struct GrowthLimitedMlxBufferPool {
inner: MlxBufferPool,
}
impl GrowthLimitedMlxBufferPool {
pub(crate) fn prepare(quota: GrowthQuota, buckets: &[GrowthBucket]) -> Result<Self> {
let mut inner = MlxBufferPool::new();
inner
.free
.try_reserve(buckets.len())
.map_err(|_| fail("free-map capacity"))?;
inner
.in_use
.try_reserve_exact(quota.roots)
.map_err(|_| fail("issued capacity"))?;
inner
.resident_buffers
.try_reserve(quota.roots)
.map_err(|_| fail("residency capacity"))?;
for b in buckets {
if inner.free.contains_key(&b.bytes) {
return Err(fail("duplicate bucket"));
}
let mut list = Vec::new();
list.try_reserve_exact(b.quota.roots)
.map_err(|_| fail("free-list capacity"))?;
inner.free.insert(b.bytes, list);
}
Ok(Self { inner })
}
pub(crate) fn attach(&mut self, binding: Binding) {
self.inner.growth = Some(binding);
}
fn binding(&self) -> &Binding {
self.inner
.growth
.as_ref()
.expect("registered facade invariant")
}
pub fn alloc(
&mut self,
device: &MlxDevice,
bytes: usize,
dtype: DType,
shape: Vec<usize>,
) -> Result<MlxBuffer> {
self.inner.alloc(device, bytes, dtype, shape)
}
pub fn alloc_uninitialized(
&mut self,
device: &MlxDevice,
bytes: usize,
dtype: DType,
shape: Vec<usize>,
) -> Result<MlxBuffer> {
self.inner.alloc_uninitialized(device, bytes, dtype, shape)
}
pub fn alloc_batch(
&mut self,
device: &MlxDevice,
requests: &[(usize, DType, Vec<usize>)],
) -> Result<Vec<MlxBuffer>> {
self.binding().check(device)?;
if requests.len() > self.binding().batch_limit()? {
return Err(fail("batch limit"));
}
for (bytes, _, shape) in requests {
self.binding().request(device, *bytes, shape.len())?;
}
self.inner.alloc_batch(device, requests.iter().cloned())
}
pub unsafe fn reset(&mut self, device: &MlxDevice) -> Result<()> {
self.binding().check(device)?;
self.inner.reset();
Ok(())
}
pub unsafe fn reset_for_ordered_gpu_reuse(&mut self, device: &MlxDevice) -> Result<()> {
self.binding().check(device)?;
self.inner.reset();
Ok(())
}
pub fn clear(&mut self, device: &MlxDevice) -> Result<()> {
self.binding().check(device)?;
if !self.inner.in_use.is_empty() {
return Err(fail("clear with issued roots"));
}
let mut removed = false;
for list in self.inner.free.values_mut() {
for buffer in list.drain(..) {
if let Some(root) = self.inner.resident_buffers.remove(&buffer_key(&buffer)) {
if let Some(set) = &self.inner.residency_set {
set.remove_allocation(root.metal());
removed = true;
}
}
}
}
if removed {
self.inner.residency_set.as_ref().unwrap().commit();
}
Ok(())
}
pub fn snapshot(&self) -> Result<LegacyGrowthSnapshot> {
self.binding().snapshot()
}
}