use core::time::Duration;
use alloc::vec::Vec;
use crate::runtime::config::autotune::AutotuneLevel;
use crate::runtime::tune::TuneInputs;
pub use crate::runtime::throughput::ResourceBound;
#[derive(Debug, Clone, PartialEq)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Bounds {
pub bounds: Vec<AutotuneBound>,
pub launch_overhead: Duration,
}
impl Eq for Bounds {}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid bounds generator",
label = "invalid bounds generator"
)]
pub trait BoundsGenerator<K, I: TuneInputs>: Send + Sync + 'static {
fn generate<'a>(&self, key: &K, inputs: &I::At<'a>) -> Bounds;
}
impl<K, Func, A> BoundsGenerator<K, A> for Func
where
A: Clone + Send + Sync + 'static,
K: 'static,
Func: Send + Sync + 'static + Fn(&K, &A) -> Bounds,
{
#[inline]
fn generate<'a>(&self, key: &K, inputs: &<A as TuneInputs>::At<'a>) -> Bounds {
(self)(key, inputs)
}
}
pub trait TimeBound {
fn time_limit(&self) -> Option<Duration>;
}
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct AutotuneBound {
pub resource: ResourceBound,
pub threshold: f32,
}
impl PartialEq for AutotuneBound {
fn eq(&self, other: &Self) -> bool {
self.resource.peak_per_s.to_bits() == other.resource.peak_per_s.to_bits()
&& self.threshold.to_bits() == other.threshold.to_bits()
&& self.resource.amount == other.resource.amount
}
}
impl Eq for AutotuneBound {}
pub use ruda_core::work::Work;
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Thresholds {
pub compute: f32,
pub memory: f32,
}
impl Thresholds {
pub const fn uniform(fraction: f32) -> Self {
Self {
compute: fraction,
memory: fraction,
}
}
pub const UNBOUNDED: Self = Self::uniform(0.0);
pub const fn for_level(level: &AutotuneLevel) -> Self {
match level {
AutotuneLevel::Minimal => Self::uniform(0.6),
AutotuneLevel::Balanced => Self::uniform(0.8),
AutotuneLevel::Extensive => Self::uniform(0.95),
AutotuneLevel::Full => Self::UNBOUNDED,
}
}
}
impl Default for Thresholds {
fn default() -> Self {
Self::uniform(1.0)
}
}
impl TimeBound for AutotuneBound {
fn time_limit(&self) -> Option<Duration> {
if self.threshold <= 0.0 || !self.threshold.is_normal() {
return None;
}
self.resource
.time_at_peak()
.map(|limit| limit.div_f64(self.threshold as f64))
}
}
impl<B: TimeBound> TimeBound for Vec<B> {
fn time_limit(&self) -> Option<Duration> {
self.iter().filter_map(|b| b.time_limit()).max()
}
}
impl TimeBound for Bounds {
fn time_limit(&self) -> Option<Duration> {
self.bounds
.time_limit()
.map(|limit| limit + self.launch_overhead)
}
}