#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MemoryLimit {
pub max_bytes: Option<i64>,
}
impl MemoryLimit {
pub const UNLIMITED: Self = Self { max_bytes: None };
pub fn from_bytes(max_bytes: u64) -> Self {
Self {
max_bytes: Some(max_bytes as _),
}
}
pub fn from_fraction_of_total(fraction: f32) -> Self {
let total_memory = crate::total_ram_in_bytes();
if total_memory == 0 {
re_log::info!("Couldn't determine total available memory. Setting no memory limit.");
Self { max_bytes: None }
} else {
let max_bytes = (fraction as f64 * total_memory as f64).round();
re_log::debug!(
"Setting memory limit to {}, which is {}% of total available memory ({}).",
re_format::format_bytes(max_bytes),
100.0 * fraction,
re_format::format_bytes(total_memory as _),
);
Self {
max_bytes: Some(max_bytes as _),
}
}
}
pub fn parse(limit: &str) -> Result<Self, String> {
if let Some(percentage) = limit.strip_suffix('%') {
let percentage = percentage
.parse::<f32>()
.map_err(|_err| format!("expected e.g. '50%', got {limit:?}"))?;
let fraction = percentage / 100.0;
Ok(Self::from_fraction_of_total(fraction))
} else {
re_format::parse_bytes(limit)
.map(|max_bytes| Self {
max_bytes: Some(max_bytes),
})
.ok_or_else(|| format!("expected e.g. '16GB', got {limit:?}"))
}
}
#[inline]
pub fn is_limited(&self) -> bool {
self.max_bytes.is_some()
}
#[inline]
pub fn is_unlimited(&self) -> bool {
self.max_bytes.is_none()
}
pub fn is_exceeded_by(&self, mem_use: &crate::MemoryUse) -> Option<f32> {
let max_bytes = self.max_bytes?;
if let Some(counted_use) = mem_use.counted {
if max_bytes < counted_use {
return Some((counted_use - max_bytes) as f32 / counted_use as f32);
}
} else if let Some(resident_use) = mem_use.resident {
re_log::warn_once!("Using resident memory use (RSS) for memory limiting, because a memory tracker was not available.");
if max_bytes < resident_use {
return Some((resident_use - max_bytes) as f32 / resident_use as f32);
}
}
None
}
}