use std::path::PathBuf;
use candle_core::DType;
use candle_core::Device;
use crate::device;
use crate::error::CanddingError;
use crate::error::Result;
use crate::hub;
use crate::hub::ModelSource;
use crate::registry::Backend;
use crate::weights::size;
const BUDGET_VAR: &str = "CANDDING_TEST_MEMORY_BUDGET";
pub(crate) fn fits(needed_bytes: u64, available_bytes: u64) -> bool {
needed_bytes <= available_bytes
}
pub(crate) fn check_download(
model: &str,
device: &Device,
source: &ModelSource,
size_gb: f32,
stored: DType,
dtype: DType,
) -> Result<()> {
if size_gb <= 0.0 || hub::is_present(source) {
return Ok(());
}
let download = (f64::from(size_gb) * 1e9) as u64;
check(model, device, size::scale(download, stored, dtype))
}
pub(crate) fn check_files(model: &str, device: &Device, files: &[(PathBuf, DType)]) -> Result<()> {
let mut needed_bytes = 0u64;
for (path, dtype) in files {
needed_bytes = needed_bytes.saturating_add(size::resident_bytes(path, *dtype)?);
}
check(model, device, needed_bytes)
}
pub(crate) fn at(paths: &[PathBuf], dtype: DType) -> Vec<(PathBuf, DType)> {
paths.iter().map(|path| (path.clone(), dtype)).collect()
}
fn check(model: &str, device: &Device, needed_bytes: u64) -> Result<()> {
let injected = std::env::var(BUDGET_VAR)
.ok()
.and_then(|bytes| bytes.parse().ok());
let available = injected.or_else(|| device::budget_bytes(device));
verdict(model, Backend::of(device), needed_bytes, available)
}
fn verdict(model: &str, backend: Backend, needed_bytes: u64, available: Option<u64>) -> Result<()> {
let Some(available_bytes) = available else {
return Ok(());
};
tracing::debug!(
model,
backend = backend.name(),
needed_bytes,
available_bytes,
"memory fit check"
);
if fits(needed_bytes, available_bytes) {
return Ok(());
}
Err(CanddingError::InsufficientMemory {
model: model.to_string(),
backend,
needed_bytes,
available_bytes,
})
}
#[cfg(test)]
mod tests;