Skip to main content

sim_lib_compute_auto/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3//! Automatic tensor compute-site selection.
4
5mod evidence;
6mod profile;
7mod store;
8
9use std::sync::Arc;
10
11use sim_kernel::{
12    AbiVersion, DefaultFactory, Export, Factory, Lib, LibManifest, LibTarget, Linker, Result,
13    Symbol, Version,
14};
15use sim_lib_compute_model::{ModeledComputeProfile, ModeledTensorExecutor};
16use sim_lib_numbers_tensor::{
17    CpuTensorExecutor, SubmissionEvidence, TensorExecError, TensorExecution, TensorExecutor,
18    TensorExecutorCard, TensorRequest, TensorSite,
19};
20
21pub use evidence::{
22    ComputeEvidenceKind, ComputePhysicalEvidence, PhysicalEvidenceError, verify_physical,
23};
24pub use profile::{
25    AutoComputeRouter, AutoRouteDecision, AutoRoutingEvent, AutoRoutingLedger, BenchmarkBounds,
26    ComputeDeviceIdentity, ComputeProfileLimits, ComputeProfileProvenance, ComputeProfileSamples,
27    ComputeThermalPowerContext, MeasuredComputeProfile, measure_bounded_profile,
28    measured_compute_profile_citizen_symbol, measured_compute_profile_shape_symbol,
29};
30pub use store::{ProfileStore, ProfileStorePolicy};
31
32/// Stable symbol for the automatic tensor executor.
33pub fn auto_executor_symbol() -> Symbol {
34    Symbol::qualified("compute", "executor/auto")
35}
36
37/// Site symbol exported by the automatic compute provider.
38pub fn compute_auto_site_symbol() -> Symbol {
39    Symbol::new("site/compute/auto")
40}
41
42/// Runtime library symbol for the automatic compute provider.
43pub fn compute_auto_lib_symbol() -> Symbol {
44    Symbol::qualified("compute", "auto-lib")
45}
46
47/// Automatic executor profile.
48#[derive(Clone, Debug, Default, PartialEq, Eq)]
49pub struct AutoComputeProfile {
50    /// Optional legacy modeled profile. When absent, auto uses CPU unless
51    /// measured evidence below proves a compatible device route.
52    pub modeled: Option<ModeledComputeProfile>,
53    /// Optional measured profile used by the conservative router.
54    pub measured: Option<MeasuredComputeProfile>,
55    /// Expected adapter/driver/backend identity for measured routing.
56    pub expected: Option<ComputeDeviceIdentity>,
57    /// Current logical tick used for staleness checks.
58    pub now_tick: u64,
59}
60
61/// Tensor executor that picks the best compatible provider and falls back to CPU.
62#[derive(Clone)]
63pub struct AutoTensorExecutor {
64    modeled: Option<ModeledTensorExecutor>,
65    decision: AutoRouteDecision,
66    ledger: AutoRoutingLedger,
67}
68
69impl AutoTensorExecutor {
70    /// Builds an automatic executor from a profile.
71    pub fn new(profile: AutoComputeProfile) -> Self {
72        let (decision, measured_modeled) = match profile.expected.clone() {
73            Some(expected) => {
74                AutoComputeRouter::new(expected, profile.now_tick).choose(profile.measured.as_ref())
75            }
76            None if profile.measured.is_some() => (AutoRouteDecision::Incompatible, None),
77            None => (AutoRouteDecision::Absent, None),
78        };
79        let modeled = measured_modeled.or(profile.modeled);
80        Self {
81            modeled: modeled.map(ModeledTensorExecutor::new),
82            decision,
83            ledger: AutoRoutingLedger::default(),
84        }
85    }
86
87    /// Returns true when the selector is using the CPU fallback.
88    pub fn uses_cpu_fallback(&self) -> bool {
89        self.modeled.is_none()
90    }
91
92    /// Returns the router decision that produced this executor.
93    pub fn route_decision(&self) -> AutoRouteDecision {
94        self.decision.clone()
95    }
96
97    /// Returns routing ledger events recorded by execute and flush calls.
98    pub fn routing_events(&self) -> Vec<AutoRoutingEvent> {
99        self.ledger.events()
100    }
101
102    fn selected(&self) -> Arc<dyn TensorExecutor> {
103        self.modeled
104            .clone()
105            .map(|executor| Arc::new(executor) as Arc<dyn TensorExecutor>)
106            .unwrap_or_else(|| Arc::new(CpuTensorExecutor::new()))
107    }
108}
109
110impl TensorExecutor for AutoTensorExecutor {
111    fn card(&self) -> TensorExecutorCard {
112        let selected = self.selected().card();
113        TensorExecutorCard::new(
114            auto_executor_symbol(),
115            if self.uses_cpu_fallback() {
116                "auto/cpu"
117            } else {
118                "auto/modeled"
119            },
120            Symbol::qualified("compute", "auto"),
121            selected.operations.to_vec(),
122            selected.device_capability,
123        )
124    }
125
126    fn execute(
127        &self,
128        cx: &mut sim_kernel::Cx,
129        request: TensorRequest,
130    ) -> std::result::Result<TensorExecution, TensorExecError> {
131        let materialization_bytes = profile::request_materialization_bytes(&request);
132        self.ledger.record(AutoRoutingEvent {
133            provider: if self.uses_cpu_fallback() {
134                "auto/cpu".to_owned()
135            } else {
136                "auto/modeled".to_owned()
137            },
138            decision: self.decision.clone(),
139            materialization_bytes,
140            synchronizations: 0,
141        });
142        self.selected().execute(cx, request)
143    }
144
145    fn flush(&self) -> std::result::Result<SubmissionEvidence, TensorExecError> {
146        let evidence = self.selected().flush()?;
147        self.ledger.record(AutoRoutingEvent {
148            provider: if self.uses_cpu_fallback() {
149                "auto/cpu".to_owned()
150            } else {
151                "auto/modeled".to_owned()
152            },
153            decision: self.decision.clone(),
154            materialization_bytes: 0,
155            synchronizations: 1,
156        });
157        Ok(evidence)
158    }
159}
160
161impl Default for AutoTensorExecutor {
162    fn default() -> Self {
163        Self::new(AutoComputeProfile::default())
164    }
165}
166
167/// Loadable library that registers the automatic compute site.
168#[derive(Clone, Debug, Default)]
169pub struct ComputeAutoLib {
170    profile: AutoComputeProfile,
171}
172
173impl ComputeAutoLib {
174    /// Builds an automatic compute library from a profile.
175    pub fn new(profile: AutoComputeProfile) -> Self {
176        Self { profile }
177    }
178}
179
180impl Lib for ComputeAutoLib {
181    fn manifest(&self) -> LibManifest {
182        LibManifest {
183            id: compute_auto_lib_symbol(),
184            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
185            abi: AbiVersion { major: 0, minor: 1 },
186            target: LibTarget::HostRegistered,
187            requires: Vec::new(),
188            capabilities: Vec::new(),
189            exports: vec![Export::Site {
190                symbol: compute_auto_site_symbol(),
191                runtime_id: None,
192            }],
193        }
194    }
195
196    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
197        let executor = Arc::new(AutoTensorExecutor::new(self.profile.clone()));
198        let site = TensorSite::new(compute_auto_site_symbol(), executor, Vec::new());
199        linker.site_value(
200            compute_auto_site_symbol(),
201            DefaultFactory.opaque(Arc::new(site))?,
202        )?;
203        Ok(())
204    }
205}
206
207/// Cookbook recipes for this lib, embedded at build time.
208pub static RECIPES: sim_cookbook::EmbeddedDir =
209    include!(concat!(env!("OUT_DIR"), "/cookbook_recipes.rs"));
210
211#[cfg(test)]
212mod tests;