Skip to main content

mnn/
schedule.rs

1use mnn_sys::*;
2use std::{ffi::CString, mem::ManuallyDrop};
3
4use crate::{BackendConfig, prelude::*};
5
6/// Backend used for running the model
7///
8/// The `ForwardType` enum is used to specify the backend that will be used for forward computation
9/// in the MNN framework. Each variant corresponds to a different backend, which may be enabled
10/// or disabled based on the features enabled in the build configuration.
11///
12/// # Variants
13///
14/// - `All`: Use all available backends.
15/// - `Auto`: Automatically select the best backend based on the current environment and hardware.
16/// - `CPU`: Use the CPU for computation.
17/// - `Metal`: Use the Metal backend for computation (requires the `metal` feature).
18/// - `OpenCL`: Use the OpenCL backend for computation (requires the `opencl` feature).
19/// - `OpenGL`: Use the OpenGL backend for computation (requires the `opengl` feature).
20/// - `Vulkan`: Use the Vulkan backend for computation (requires the `vulkan` feature).
21/// - `CoreML`: Use the CoreML backend for computation (requires the `coreml` feature).
22///
23/// # Example
24///
25/// ```rust
26/// use mnn::schedule::ForwardType;
27///
28/// let forward_type = ForwardType::Auto;
29/// println!("Selected forward type: {:?}", forward_type);
30/// ```
31///
32/// # Note
33///
34/// The availability of certain variants depends on the features enabled during the build.
35/// For example, the `Metal` variant is only available if the `metal` feature is enabled.
36#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub enum ForwardType {
39    /// Use all available backends.
40    All,
41    #[default]
42    /// Try to automatically select the best backend based on the current environment and hardware.
43    Auto,
44    /// Use the CPU for computation.
45    CPU,
46    #[cfg(feature = "metal")]
47    /// Use the Metal backend for computation.
48    Metal,
49    #[cfg(feature = "opencl")]
50    /// Use the OpenCL backend for computation.
51    OpenCL,
52    /// Use the Vulkan backend for computation.
53    #[cfg(feature = "vulkan")]
54    Vulkan,
55    /// Use the CUDA backend for computation.
56    #[cfg(feature = "cuda")]
57    Cuda,
58    /// Use the CoreML backend for computation.
59    #[cfg(feature = "coreml")]
60    CoreML,
61}
62
63impl ForwardType {
64    /// Convert the `ForwardType` enum to the corresponding C++ `MNNForwardType` enum.
65    fn to_mnn_sys(self) -> MNNForwardType {
66        match self {
67            ForwardType::Auto => MNNForwardType::MNN_FORWARD_AUTO,
68            ForwardType::All => MNNForwardType::MNN_FORWARD_ALL,
69            ForwardType::CPU => MNNForwardType::MNN_FORWARD_CPU,
70            #[cfg(feature = "metal")]
71            ForwardType::Metal => MNNForwardType::MNN_FORWARD_METAL,
72            #[cfg(feature = "opencl")]
73            ForwardType::OpenCL => MNNForwardType::MNN_FORWARD_OPENCL,
74            #[cfg(feature = "opengl")]
75            ForwardType::OpenGL => MNNForwardType::MNN_FORWARD_OPENGL,
76            #[cfg(feature = "vulkan")]
77            ForwardType::Vulkan => MNNForwardType::MNN_FORWARD_VULKAN,
78            #[cfg(feature = "cuda")]
79            ForwardType::Cuda => MNNForwardType::MNN_FORWARD_CUDA,
80            #[cfg(feature = "coreml")]
81            ForwardType::CoreML => MNNForwardType::MNN_FORWARD_NN,
82        }
83    }
84
85    fn from_mnn_sys(mode: MNNForwardType) -> Self {
86        match mode {
87            MNNForwardType::MNN_FORWARD_AUTO => ForwardType::Auto,
88            MNNForwardType::MNN_FORWARD_ALL => ForwardType::All,
89            MNNForwardType::MNN_FORWARD_CPU => ForwardType::CPU,
90            #[cfg(feature = "metal")]
91            MNNForwardType::MNN_FORWARD_METAL => ForwardType::Metal,
92            #[cfg(feature = "opencl")]
93            MNNForwardType::MNN_FORWARD_OPENCL => ForwardType::OpenCL,
94            #[cfg(feature = "opengl")]
95            MNNForwardType::MNN_FORWARD_OPENGL => ForwardType::OpenGL,
96            #[cfg(feature = "vulkan")]
97            MNNForwardType::MNN_FORWARD_VULKAN => ForwardType::Vulkan,
98            #[cfg(feature = "cuda")]
99            MNNForwardType::MNN_FORWARD_CUDA => ForwardType::Cuda,
100            #[cfg(feature = "coreml")]
101            MNNForwardType::MNN_FORWARD_NN => ForwardType::CoreML,
102            _ => ForwardType::Auto,
103        }
104    }
105
106    /// List all available `ForwardType` variants as string slices.
107    fn list() -> Vec<&'static str> {
108        vec![
109            "auto",
110            "all",
111            "cpu",
112            #[cfg(feature = "metal")]
113            "metal",
114            #[cfg(feature = "opencl")]
115            "opencl",
116            #[cfg(feature = "opengl")]
117            "opengl",
118            #[cfg(feature = "vulkan")]
119            "vulkan",
120            #[cfg(feature = "cuda")]
121            "cuda",
122            #[cfg(feature = "coreml")]
123            "coreml",
124        ]
125    }
126
127    /// Convert the `ForwardType` enum to a string slice.
128    pub fn to_str(self) -> &'static str {
129        match self {
130            ForwardType::Auto => "auto",
131            ForwardType::All => "all",
132            ForwardType::CPU => "cpu",
133            #[cfg(feature = "metal")]
134            ForwardType::Metal => "metal",
135            #[cfg(feature = "opencl")]
136            ForwardType::OpenCL => "opencl",
137            #[cfg(feature = "opengl")]
138            ForwardType::OpenGL => "opengl",
139            #[cfg(feature = "vulkan")]
140            ForwardType::Vulkan => "vulkan",
141            #[cfg(feature = "cuda")]
142            ForwardType::Cuda => "cuda",
143            #[cfg(feature = "coreml")]
144            ForwardType::CoreML => "coreml",
145        }
146    }
147}
148
149impl core::str::FromStr for ForwardType {
150    type Err = MNNError;
151
152    fn from_str(s: &str) -> Result<Self, Self::Err> {
153        match s {
154            "auto" => Ok(ForwardType::Auto),
155            "all" => Ok(ForwardType::All),
156            "cpu" => Ok(ForwardType::CPU),
157            #[cfg(feature = "metal")]
158            "metal" => Ok(ForwardType::Metal),
159            #[cfg(feature = "opencl")]
160            "opencl" => Ok(ForwardType::OpenCL),
161            #[cfg(feature = "opengl")]
162            "opengl" => Ok(ForwardType::OpenGL),
163            #[cfg(feature = "vulkan")]
164            "vulkan" => Ok(ForwardType::Vulkan),
165            #[cfg(feature = "cuda")]
166            "cuda" => Ok(ForwardType::Cuda),
167            #[cfg(feature = "coreml")]
168            "coreml" => Ok(ForwardType::CoreML),
169            _ => Err(MNNError::new(crate::ErrorKind::ParseError)
170                .attach(format!(
171                    "Invalid ForwardType: {s}, maybe you might need to enable feature {s}"
172                ))
173                .attach(format!(
174                    "Valid ForwardType: {}",
175                    ForwardType::list().join(", ")
176                ))),
177        }
178    }
179}
180
181/// Configuration for scheduling the forward computation in MNN.
182///
183/// The `ScheduleConfig` struct is used to configure various parameters for scheduling the forward
184/// computation in the MNN framework. It allows setting the type of backend, the number of threads,
185/// the mode of computation, and other options.
186///
187/// # Example
188///
189/// ```rust
190/// use mnn::schedule::{ScheduleConfig, ForwardType};
191///
192/// let mut config = ScheduleConfig::new();
193/// config.set_type(ForwardType::Auto);
194/// config.set_num_threads(4);
195/// config.set_mode(0);
196/// ```
197///
198/// # Fields
199///
200/// - `inner`: A raw pointer to the underlying `MNNScheduleConfig` structure.
201/// - `backend_config`: Specifies backend-specific configurations.
202/// - `__marker`: A marker to ensure the struct is `!Send` by default.
203///
204/// # Methods
205///
206/// - `new() -> Self`: Creates a new `ScheduleConfig` with default settings.
207/// - `as_ptr_mut(&mut self) -> *mut MNNScheduleConfig`: Returns a mutable raw pointer to the underlying `MNNScheduleConfig`.
208/// - `set_save_tensors(&mut self, save_tensors: &[&str]) -> Result<()>`: Sets the tensors to be saved during computation.
209/// - `set_type(&mut self, forward_type: ForwardType)`: Sets the type of backend to be used for computation.
210/// - `set_num_threads(&mut self, num_threads: i32)`: Sets the number of threads to be used for computation.
211/// - `set_mode(&mut self, mode: i32)`: Sets the mode of computation.
212/// - `set_backup_type(&mut self, backup_type: ForwardType)`: Sets the backup type of backend to be used if the primary backend fails.
213/// - `set_backend_config(&mut self, backend_config: impl Into<Option<BackendConfig>>)`: Sets the backend-specific configuration.
214///
215/// # Safety
216///
217/// The `ScheduleConfig` struct contains raw pointers and interacts with the underlying C API of MNN.
218/// Users should be cautious when using this struct to avoid undefined behavior.
219///
220/// # Warning
221///
222/// **Warning:** The `Drop` implementation for `ScheduleConfig` ensures that the underlying `MNNScheduleConfig`
223/// is properly destroyed when the struct goes out of scope. Users should not manually free the `inner` pointer.
224pub struct ScheduleConfig {
225    pub(crate) inner: *mut MNNScheduleConfig,
226    pub(crate) backend_config: Option<BackendConfig>,
227    pub(crate) __marker: core::marker::PhantomData<()>,
228}
229
230impl core::fmt::Debug for ScheduleConfig {
231    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232        f.debug_struct("ScheduleConfig")
233            .field("type", &self.get_type())
234            .field("backup_type", &self.get_backup_type())
235            .field("backend_config", &self.backend_config)
236            .finish()
237    }
238}
239
240#[cfg(feature = "serde")]
241impl serde::Serialize for ScheduleConfig {
242    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
243        use serde::ser::SerializeStruct;
244        let mut state = serializer.serialize_struct("ScheduleConfig", 3)?;
245        state.serialize_field("type", &self.get_type())?;
246        state.serialize_field("backup_type", &self.get_backup_type())?;
247        state.serialize_field("backend_config", &self.backend_config)?;
248        state.end()
249    }
250}
251
252impl Clone for ScheduleConfig {
253    fn clone(&self) -> Self {
254        unsafe {
255            let inner = mnnsc_clone(self.inner);
256            Self {
257                inner,
258                backend_config: self.backend_config.clone(),
259                __marker: core::marker::PhantomData,
260            }
261        }
262    }
263}
264
265impl Drop for ScheduleConfig {
266    fn drop(&mut self) {
267        unsafe {
268            mnn_sys::mnnsc_destroy(self.inner);
269        }
270    }
271}
272
273unsafe impl Send for ScheduleConfig {}
274
275impl Default for ScheduleConfig {
276    fn default() -> Self {
277        Self::new()
278    }
279}
280
281impl ScheduleConfig {
282    /// Returns a mutable raw pointer to the underlying `MNNScheduleConfig`.
283    pub fn as_ptr_mut(&mut self) -> *mut MNNScheduleConfig {
284        self.inner
285    }
286
287    /// Creates a new `ScheduleConfig` with default settings.
288    pub fn new() -> Self {
289        unsafe {
290            let inner = mnnsc_create();
291            Self {
292                inner,
293                backend_config: None,
294                __marker: core::marker::PhantomData,
295            }
296        }
297    }
298
299    /// Sets the tensors to be saved during computation.
300    ///
301    /// # Arguments
302    ///
303    /// - `save_tensors`: A slice of tensor names to be saved.
304    ///
305    /// # Errors
306    ///
307    /// Returns an error if any of the tensor names contain null bytes.
308    pub fn set_save_tensors(&mut self, save_tensors: &[&str]) -> Result<&mut Self> {
309        let vec_cstring = save_tensors
310            .iter()
311            .map(|s| std::ffi::CString::new(*s).map_err(|e| error!(ErrorKind::AsciiError, e)))
312            .collect::<Result<Vec<_>>>()?;
313        let vec_cstr = vec_cstring
314            .iter()
315            .map(|s: &CString| s.as_c_str().as_ptr())
316            .collect::<Vec<_>>();
317        unsafe { mnnsc_set_save_tensors(self.inner, vec_cstr.as_ptr(), vec_cstr.len()) }
318        Ok(self)
319    }
320
321    /// Sets the type of backend to be used for computation.
322    ///
323    /// # Arguments
324    ///
325    /// - `forward_type`: The type of backend to be used.
326    pub fn set_type(&mut self, forward_type: ForwardType) -> &mut Self {
327        unsafe {
328            mnnsc_set_type(self.inner, forward_type.to_mnn_sys());
329        }
330        self
331    }
332
333    /// Sets the type of backend to be used for computation.
334    pub fn with_type(mut self, forward_type: ForwardType) -> Self {
335        self.set_type(forward_type);
336        self
337    }
338
339    /// Gets the type of backend to be used for computation.
340    pub fn get_type(&self) -> ForwardType {
341        unsafe { ForwardType::from_mnn_sys(mnnsc_get_type(self.inner)) }
342    }
343
344    /// Sets the number of threads to be used for computation.
345    ///
346    /// # Arguments
347    ///
348    /// - `num_threads`: The number of threads to be used.
349    pub fn set_num_threads(&mut self, num_threads: i32) -> &mut Self {
350        unsafe {
351            mnnsc_set_num_threads(self.inner, num_threads);
352        }
353        self
354    }
355
356    /// Sets the number of threads to be used for computation.
357    pub fn with_num_threads(mut self, num_threads: i32) -> Self {
358        self.set_num_threads(num_threads);
359        self
360    }
361
362    /// Sets the mode of computation.
363    ///
364    /// # Arguments
365    ///
366    /// - `mode`: The mode of computation.
367    pub fn set_mode(&mut self, mode: i32) -> &mut Self {
368        unsafe {
369            mnnsc_set_mode(self.inner, mode);
370        }
371        self
372    }
373
374    /// Sets the mode of computation.
375    pub fn with_mode(mut self, mode: i32) -> Self {
376        self.set_mode(mode);
377        self
378    }
379
380    /// Sets the backup type of backend to be used if the primary backend fails.
381    ///
382    /// # Arguments
383    ///
384    /// - `backup_type`: The backup type of backend to be used.
385    pub fn set_backup_type(&mut self, backup_type: ForwardType) -> &mut Self {
386        unsafe {
387            mnnsc_set_backup_type(self.inner, backup_type.to_mnn_sys());
388        }
389        self
390    }
391
392    /// Sets the backup type of backend to be used if the primary backend fails.
393    pub fn with_backup_type(mut self, backup_type: ForwardType) -> Self {
394        self.set_backup_type(backup_type);
395        self
396    }
397
398    /// Gets the backup type of backend to be used if the primary backend fails.
399    pub fn get_backup_type(&self) -> ForwardType {
400        unsafe { ForwardType::from_mnn_sys(mnnsc_get_backup_type(self.inner)) }
401    }
402
403    /// Sets the backend-specific configuration.
404    ///
405    /// # Arguments
406    ///
407    /// - `backend_config`: specifies additional backend-specific configurations.
408    pub fn set_backend_config(
409        &mut self,
410        backend_config: impl Into<Option<BackendConfig>>,
411    ) -> &mut Self {
412        self.backend_config = backend_config.into();
413        let ptr = if let Some(ref b) = self.backend_config {
414            b.inner
415        } else {
416            core::ptr::null_mut()
417        };
418        unsafe {
419            mnnsc_set_backend_config(self.inner, ptr);
420        }
421        self
422    }
423
424    /// Sets the backend-specific configuration.
425    pub fn with_backend_config(mut self, backend_config: impl Into<Option<BackendConfig>>) -> Self {
426        self.set_backend_config(backend_config);
427        self
428    }
429}
430
431/// A list of `ScheduleConfig` objects to be used for scheduling the forward computation in MNN.
432#[derive(Debug)]
433pub struct ScheduleConfigs {
434    pub(crate) inner: Vec<*const MNNScheduleConfig>,
435    pub(crate) backend_configs: Vec<Option<BackendConfig>>,
436}
437
438impl Drop for ScheduleConfigs {
439    fn drop(&mut self) {
440        unsafe {
441            for i in self.inner.iter() {
442                mnnsc_destroy(*i.cast());
443            }
444        }
445    }
446}
447
448impl ScheduleConfigs {
449    /// Pushed a new `ScheduleConfig` to the list of configurations.
450    pub fn push(&mut self, config: ScheduleConfig) {
451        let mut config = ManuallyDrop::new(config);
452        self.inner.push(config.inner);
453        self.backend_configs.push(config.backend_config.take());
454    }
455
456    /// Creates a new (empty) `ScheduleConfigs` with the specified capacity.
457    pub fn with_capacity(capacity: usize) -> Self {
458        Self {
459            inner: Vec::with_capacity(capacity),
460            backend_configs: Vec::with_capacity(capacity),
461        }
462    }
463
464    /// Creates a new (empty) `ScheduleConfigs` with default settings.
465    pub const fn new() -> Self {
466        Self {
467            inner: Vec::new(),
468            backend_configs: Vec::new(),
469        }
470    }
471}
472
473impl Default for ScheduleConfigs {
474    fn default() -> Self {
475        Self::new()
476    }
477}
478
479impl FromIterator<ScheduleConfig> for ScheduleConfigs {
480    fn from_iter<T: IntoIterator<Item = ScheduleConfig>>(iter: T) -> Self {
481        let iter = iter.into_iter();
482        let mut ret = Self::with_capacity(iter.size_hint().1.unwrap_or_default());
483        iter.for_each(|item| {
484            ret.push(item);
485        });
486        ret
487    }
488}
489
490unsafe impl Send for ScheduleConfigs {}