use alloc::vec::Vec;
use core::fmt;
use vize_l0::Span;
use vize_l0::pass::{Remark, RemarkArg, RemarkSink};
use super::measure::{Metric, Metrics};
use super::tier::OptTier;
use crate::op::OpId;
use crate::placement::Placement;
pub const EXTRACT_PASS: &str = "extract-placements";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DecisionKind {
Applied,
Missed,
}
impl DecisionKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Applied => "applied",
Self::Missed => "missed",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Reason {
Committed,
Regressed(Metric),
NoImprovement,
BudgetExhausted,
NotContiguous,
Subsumed,
}
impl Reason {
pub const ALL: [Self; 8] = [
Self::Committed,
Self::Regressed(Metric::ReactiveEdge),
Self::Regressed(Metric::UpdatePath),
Self::Regressed(Metric::EmittedSize),
Self::NoImprovement,
Self::BudgetExhausted,
Self::NotContiguous,
Self::Subsumed,
];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Committed => "committed",
Self::Regressed(Metric::ReactiveEdge) => "regressed-reactive-edge",
Self::Regressed(Metric::UpdatePath) => "regressed-update-path",
Self::Regressed(Metric::EmittedSize) => "regressed-emitted-size",
Self::NoImprovement => "no-improvement",
Self::BudgetExhausted => "budget-exhausted",
Self::NotContiguous => "not-contiguous",
Self::Subsumed => "subsumed",
}
}
#[must_use]
pub fn parse(text: &str) -> Option<Self> {
Self::ALL.into_iter().find(|reason| reason.as_str() == text)
}
}
impl fmt::Display for Reason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Delta {
pub emitted_size: i64,
pub reactive_edges: i64,
pub update_path: i64,
}
impl Delta {
#[must_use]
pub fn between(before: Metrics, after: Metrics) -> Self {
let diff = |metric| after.get(metric) as i64 - before.get(metric) as i64;
Self {
emitted_size: diff(Metric::EmittedSize),
reactive_edges: diff(Metric::ReactiveEdge),
update_path: diff(Metric::UpdatePath),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Decision {
pub op: OpId,
pub span: Span,
pub placement: Placement,
pub kind: DecisionKind,
pub reason: Reason,
pub delta: Delta,
pub budget_left: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Extraction {
pub tier: OptTier,
pub candidate_budget: u32,
pub budget_left: u32,
pub before: Metrics,
pub after: Metrics,
pub decisions: Vec<Decision>,
}
impl Extraction {
pub fn remark<S: RemarkSink>(&self, sink: &mut S) {
if !S::ENABLED {
return;
}
for decision in &self.decisions {
let args = [
RemarkArg::str("reason", decision.reason.as_str()),
RemarkArg::int("emitted-size", decision.delta.emitted_size),
RemarkArg::int("reactive-edges", decision.delta.reactive_edges),
RemarkArg::int("update-path", decision.delta.update_path),
RemarkArg::int("budget-left", i64::from(decision.budget_left)),
];
let name = decision.placement.as_str();
let remark = match decision.kind {
DecisionKind::Applied => Remark::applied(name, decision.span, &args),
DecisionKind::Missed => Remark::missed(name, decision.span, &args),
};
sink.emit(&remark);
}
}
}