use alloc::vec::Vec;
use crate::runtime::throughput::{MemoryAccess, MemorySpec, ThroughputKey, ThroughputMode, ThroughputValue};
pub const MIN_WORKING_SET: u64 = 8 * 1024;
pub fn working_set_sweep(cap: u64) -> Vec<u64> {
if cap < MIN_WORKING_SET {
return alloc::vec![cap];
}
let mut sizes = Vec::new();
let mut bytes = MIN_WORKING_SET;
while bytes <= cap {
sizes.push(bytes);
match bytes.checked_mul(2) {
Some(next) => bytes = next,
None => break,
}
}
sizes
}
pub fn sweep_size(bytes: u64) -> u64 {
let bytes = bytes.max(MIN_WORKING_SET);
1 << (u64::BITS - 1 - bytes.leading_zeros())
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct MemoryPoint {
pub bytes: u64,
pub value: ThroughputValue,
}
#[derive(PartialEq, Clone, Debug)]
pub struct MemoryCurve {
access: MemoryAccess,
points: Vec<MemoryPoint>,
}
impl MemoryCurve {
pub fn new(access: MemoryAccess, points: impl IntoIterator<Item = MemoryPoint>) -> Self {
let mut curve = Self {
access,
points: Vec::new(),
};
curve.points = points
.into_iter()
.filter(|point| {
let rate = curve.rate(point);
point.bytes > 0 && rate.is_finite() && rate > 0.0
})
.collect();
curve.points.sort_unstable_by_key(|point| point.bytes);
curve.points.dedup_by_key(|point| point.bytes);
curve
}
pub fn points(&self) -> &[MemoryPoint] {
&self.points
}
pub fn ceiling_at(&self, bytes: u64) -> Option<f64> {
let first = self.points.first()?;
let last = self.points.last()?;
if bytes <= first.bytes {
return Some(self.rate(first));
}
if bytes >= last.bytes {
return Some(self.rate(last));
}
let above = self.points.partition_point(|point| point.bytes <= bytes);
let (low, high) = (&self.points[above - 1], &self.points[above]);
let span = log2(high.bytes) - log2(low.bytes);
let weight = (log2(bytes) - log2(low.bytes)) / span;
Some(self.rate(low) + weight * (self.rate(high) - self.rate(low)))
}
fn rate(&self, point: &MemoryPoint) -> f64 {
let mode = ThroughputMode::Memory(MemorySpec::new(self.access, point.bytes));
point.value.bytes_per_s(&ThroughputKey { mode })
}
}
fn log2(bytes: u64) -> f64 {
let bytes = bytes.max(1);
let exponent = 63 - bytes.leading_zeros();
let mantissa = bytes as f64 / (1u64 << exponent) as f64;
exponent as f64 + (mantissa - 1.0)
}