1use std::any::Any;
2use std::fmt::Debug;
3
4use downcast_rs::Downcast;
5use dyn_clone::DynClone;
6use lazy_static::lazy_static;
7use tract_linalg::multithread::Executor;
8
9use crate::internal::*;
10
11#[derive(Clone, Debug, Default)]
12pub struct RunOptions {
13 pub skip_order_opt_ram: bool,
15
16 pub executor: Option<Executor>,
18
19 pub memory_sizing_hints: Option<SymbolValues>,
21}
22
23pub trait Runtime: Debug + Send + Sync + 'static {
24 fn name(&self) -> StaticName;
25 fn prepare(&self, model: TypedModel) -> TractResult<Box<dyn Runnable>> {
26 self.prepare_with_options(model, &Default::default())
27 }
28 fn check(&self) -> TractResult<()>;
29 fn prepare_with_options(
30 &self,
31 model: TypedModel,
32 options: &RunOptions,
33 ) -> TractResult<Box<dyn Runnable>>;
34}
35
36pub trait Runnable: Any + Downcast + Debug + Send + Sync + 'static {
37 fn run(&self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
38 self.spawn()?.run(inputs)
39 }
40 fn spawn(&self) -> TractResult<Box<dyn State>>;
41 fn input_count(&self) -> usize {
42 self.typed_model().context("Fallback implementation on typed_model()").unwrap().inputs.len()
43 }
44 fn output_count(&self) -> usize {
45 self.typed_model()
46 .context("Fallback implementation on typed_model()")
47 .unwrap()
48 .outputs
49 .len()
50 }
51 fn input_fact(&self, ix: usize) -> TractResult<&TypedFact> {
52 self.typed_model()
53 .context("Fallback implementation on typed_model()")
54 .unwrap()
55 .input_fact(ix)
56 }
57 fn output_fact(&self, ix: usize) -> TractResult<&TypedFact> {
58 self.typed_model()
59 .context("Fallback implementation on typed_model()")
60 .unwrap()
61 .output_fact(ix)
62 }
63 fn properties(&self) -> &HashMap<String, Arc<Tensor>> {
64 lazy_static! {
65 static ref NO_PROPERTIES: HashMap<String, Arc<Tensor>> = Default::default();
66 };
67 self.typed_model().map(|model| &model.properties).unwrap_or(&NO_PROPERTIES)
68 }
69
70 fn typed_plan(&self) -> Option<&Arc<TypedSimplePlan>>;
71 fn typed_model(&self) -> Option<&Arc<TypedModel>>;
72}
73impl_downcast!(Runnable);
74
75pub trait State: Any + Downcast + Debug + Send + DynClone + 'static {
76 fn run(&mut self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>>;
77
78 fn resolve_symbol(&mut self, symbol: &Symbol, value: i64) -> TractResult<()> {
82 let _ = (symbol, value);
83 bail!("{self:?} can not resolve a symbol")
84 }
85
86 fn seat(&mut self, seating: Seating) -> TractResult<()> {
90 let _ = seating;
91 bail!("{self:?} can not seat lanes")
92 }
93
94 fn reset_lanes(&mut self, lanes: &[LaneId]) -> TractResult<()> {
96 let _ = lanes;
97 bail!("{self:?} can not reset lanes")
98 }
99
100 fn runnable(&self) -> &dyn Runnable;
101
102 fn input_count(&self) -> usize {
103 self.runnable().input_count()
104 }
105
106 fn output_count(&self) -> usize {
107 self.runnable().output_count()
108 }
109}
110impl_downcast!(State);
111dyn_clone::clone_trait_object!(State);
112
113#[derive(Debug)]
114pub struct DefaultRuntime;
115
116impl Runtime for DefaultRuntime {
117 fn name(&self) -> StaticName {
118 Cow::Borrowed("cpu")
119 }
120
121 fn prepare_with_options(
122 &self,
123 model: TypedModel,
124 options: &RunOptions,
125 ) -> TractResult<Box<dyn Runnable>> {
126 let model = model.into_optimized()?;
127 Ok(Box::new(TypedSimplePlan::new_with_options(model, options)?))
128 }
129
130 fn check(&self) -> TractResult<()> {
131 Ok(())
132 }
133}
134
135impl Runnable for Arc<TypedRunnableModel> {
136 fn spawn(&self) -> TractResult<Box<dyn State>> {
137 Ok(Box::new(self.spawn()?))
138 }
139
140 fn typed_plan(&self) -> Option<&Self> {
141 Some(self)
142 }
143
144 fn typed_model(&self) -> Option<&Arc<TypedModel>> {
145 Some(&self.model)
146 }
147
148 fn input_count(&self) -> usize {
149 self.model.inputs.len()
150 }
151
152 fn output_count(&self) -> usize {
153 self.model.outputs.len()
154 }
155
156 fn input_fact(&self, ix: usize) -> TractResult<&TypedFact> {
157 self.model.input_fact(ix)
158 }
159 fn output_fact(&self, ix: usize) -> TractResult<&TypedFact> {
160 self.model.output_fact(ix)
161 }
162}
163
164impl State for TypedSimpleState {
165 fn run(&mut self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
166 self.run(inputs)
167 }
168
169 fn resolve_symbol(&mut self, symbol: &Symbol, value: i64) -> TractResult<()> {
170 self.turn_state.resolved_symbols.set(symbol, value);
171 Ok(())
172 }
173
174 fn seat(&mut self, seating: Seating) -> TractResult<()> {
175 self.seat(seating);
176 Ok(())
177 }
178
179 fn reset_lanes(&mut self, lanes: &[LaneId]) -> TractResult<()> {
180 self.reset_lanes(lanes)
181 }
182
183 fn runnable(&self) -> &dyn Runnable {
184 &self.plan
185 }
186}
187
188pub struct InventorizedRuntime(pub &'static dyn Runtime);
189
190impl Runtime for InventorizedRuntime {
191 fn name(&self) -> StaticName {
192 self.0.name()
193 }
194
195 fn prepare_with_options(
196 &self,
197 model: TypedModel,
198 options: &RunOptions,
199 ) -> TractResult<Box<dyn Runnable>> {
200 self.0.prepare_with_options(model, options)
201 }
202
203 fn check(&self) -> TractResult<()> {
204 self.0.check()
205 }
206}
207
208impl Debug for InventorizedRuntime {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 self.0.fmt(f)
211 }
212}
213
214inventory::collect!(InventorizedRuntime);
215
216pub fn runtimes() -> impl Iterator<Item = &'static dyn Runtime> {
217 inventory::iter::<InventorizedRuntime>().filter(|rt| rt.check().is_ok()).map(|ir| ir.0)
218}
219
220const GPU_RUNTIME_NAMES: &[&str] = &["metal", "cuda"];
223
224pub fn runtime_for_name(s: &str) -> TractResult<Option<&'static dyn Runtime>> {
225 let s = if s == "default" { "cpu" } else { s };
228 if s == "gpu" || s == "gpu-or-cpu" {
229 let mut last_check_err: Option<TractError> = None;
230 for name in GPU_RUNTIME_NAMES {
231 let Some(rt) = inventory::iter::<InventorizedRuntime>().find(|rt| rt.name() == *name)
232 else {
233 continue;
234 };
235 match rt.check() {
236 Ok(()) => return Ok(Some(rt.0)),
237 Err(e) => last_check_err = Some(e),
238 }
239 }
240 if s == "gpu" {
241 let detail =
242 last_check_err.map(|e| format!(" (last backend error: {e:#})")).unwrap_or_default();
243 bail!("Runtime `gpu` requested but no GPU backend is available{detail}");
244 }
245 return runtime_for_name("cpu");
247 }
248 rule_if_some!(rt = inventory::iter::<InventorizedRuntime>().find(|rt| rt.name() == s));
249 rt.check()?;
250 Ok(Some(rt.0))
251}
252
253#[macro_export]
254macro_rules! register_runtime {
255 ($type: ty= $val:expr) => {
256 static D: $type = $val;
257 inventory::submit! { $crate::runtime::InventorizedRuntime(&D) }
258 };
259}
260
261register_runtime!(DefaultRuntime = DefaultRuntime);