Skip to main content

runmat_execution/placement/
cost.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
4#[serde(rename_all = "snake_case")]
5pub enum EstimateConfidence {
6    Prior,
7    Low,
8    Medium,
9    High,
10    Exact,
11}
12
13impl EstimateConfidence {
14    pub const fn uncertainty_basis_points(self) -> u32 {
15        match self {
16            Self::Prior => 2_500,
17            Self::Low => 1_500,
18            Self::Medium => 500,
19            Self::High => 100,
20            Self::Exact => 0,
21        }
22    }
23}
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum EstimateSource {
28    StaticPrior,
29    Calibration,
30    Observation,
31    Compiler,
32    Provider,
33    Synthetic,
34}
35
36/// Complete additive cost decomposition for one legal execution candidate.
37///
38/// Components are deliberately executor-neutral. Providers retain ownership of
39/// kernel scheduling, while placement retains ownership of comparing complete
40/// candidates and residency transitions.
41#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
42#[serde(deny_unknown_fields)]
43pub struct ExecutionCostComponents {
44    pub compile_or_prepare_ns: u64,
45    pub upload_ns: u64,
46    pub allocation_ns: u64,
47    pub queue_ns: u64,
48    pub execution_ns: u64,
49    pub synchronization_ns: u64,
50    pub download_ns: u64,
51    pub downstream_ns: u64,
52}
53
54impl ExecutionCostComponents {
55    pub fn checked_total_ns(self) -> Option<u64> {
56        [
57            self.compile_or_prepare_ns,
58            self.upload_ns,
59            self.allocation_ns,
60            self.queue_ns,
61            self.execution_ns,
62            self.synchronization_ns,
63            self.download_ns,
64            self.downstream_ns,
65        ]
66        .into_iter()
67        .try_fold(0_u64, u64::checked_add)
68    }
69}
70
71#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
72#[serde(deny_unknown_fields)]
73pub struct ExecutionCostEstimate {
74    pub components: ExecutionCostComponents,
75    pub scratch_bytes: u64,
76    pub confidence: EstimateConfidence,
77    pub source: EstimateSource,
78}
79
80impl ExecutionCostEstimate {
81    pub fn checked_total_ns(self) -> Option<u64> {
82        self.components.checked_total_ns()
83    }
84
85    pub fn checked_risk_adjusted_ns(self) -> Option<u64> {
86        let total = self.checked_total_ns()?;
87        let uncertainty = total
88            .checked_mul(u64::from(self.confidence.uncertainty_basis_points()))?
89            .checked_add(9_999)?
90            / 10_000;
91        total.checked_add(uncertainty)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn component_totals_and_uncertainty_are_checked() {
101        let estimate = ExecutionCostEstimate {
102            components: ExecutionCostComponents {
103                execution_ns: 100,
104                upload_ns: 20,
105                ..ExecutionCostComponents::default()
106            },
107            scratch_bytes: 0,
108            confidence: EstimateConfidence::Medium,
109            source: EstimateSource::Synthetic,
110        };
111        assert_eq!(estimate.checked_total_ns(), Some(120));
112        assert_eq!(estimate.checked_risk_adjusted_ns(), Some(126));
113
114        let overflow = ExecutionCostComponents {
115            execution_ns: u64::MAX,
116            upload_ns: 1,
117            ..ExecutionCostComponents::default()
118        };
119        assert_eq!(overflow.checked_total_ns(), None);
120    }
121}