From 90e9cb95f9b50c10a350331ef33afe112bbe6c24 Mon Sep 17 00:00:00 2001 From: Adam Jesionowski Date: Fri, 23 Apr 2021 12:21:12 -0700 Subject: [PATCH] Start ML Coordinator. ML Coordinator needs to be able to load and unload models, which means verifying and creating any necessary transfer buffers and other data structures. First draft just keeps track of model references. ML Coordinator needs to be able to execute models. Actual execution is abstracted away from ExecutiveInterface. Deletes fake_executive and moves to this file in the test section, as this is currently the only user. Change-Id: Ib2de5bc5761ccffe244e0bc0a02c6d2805cc00c6 GitOrigin-RevId: dc4de0085b36094ec6ac10c3a7f4fe0c8dff1627 --- .../components/MlCoordinator/Cargo.toml | 4 +- .../MlCoordinator/fake-executive/src/lib.rs | 44 ----- .../MlCoordinator/ml-common/src/lib.rs | 14 +- .../Cargo.toml | 5 +- .../MlCoordinator/ml-coordinator/src/lib.rs | 161 ++++++++++++++++++ 5 files changed, 178 insertions(+), 50 deletions(-) delete mode 100644 apps/system/components/MlCoordinator/fake-executive/src/lib.rs rename apps/system/components/MlCoordinator/{fake-executive => ml-coordinator}/Cargo.toml (59%) create mode 100644 apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs diff --git a/apps/system/components/MlCoordinator/Cargo.toml b/apps/system/components/MlCoordinator/Cargo.toml index 56ee0c3..bc1937e 100644 --- a/apps/system/components/MlCoordinator/Cargo.toml +++ b/apps/system/components/MlCoordinator/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ - "fake-executive", - "ml-common" + "ml-common", + "ml-coordinator", ] diff --git a/apps/system/components/MlCoordinator/fake-executive/src/lib.rs b/apps/system/components/MlCoordinator/fake-executive/src/lib.rs deleted file mode 100644 index 87d80ab..0000000 --- a/apps/system/components/MlCoordinator/fake-executive/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use ml_common as ml; -use ml_common::ExecutiveInterface; - -pub struct FakeExecutive { - output_memory: [u8; 512], - fake_error: Option, -} - -impl ml::ExecutiveInterface for FakeExecutive { - fn run_model(&self, _model: &ml::Model) -> Result<&[u8], ml::ExecutionError> { - match self.fake_error { - Some(err) => Err(err), - None => Ok(&self.output_memory), - } - } -} - -#[test] -fn return_ok() { - let exec = FakeExecutive { - output_memory: [0xAD; 512], - fake_error: None, - }; - let model = ml::Model { - output_activations_len: 512, - }; - - let res = exec.run_model(&model); - assert!(res.is_ok()); - assert_eq!(res.unwrap()[0], 0xAD) -} - -#[test] -fn return_err() { - let exec = FakeExecutive { - output_memory: [0; 512], - fake_error: Some(ml::ExecutionError::CoreReset), - }; - let model = ml::Model { - output_activations_len: 512, - }; - - assert!(exec.run_model(&model).is_err()); -} diff --git a/apps/system/components/MlCoordinator/ml-common/src/lib.rs b/apps/system/components/MlCoordinator/ml-common/src/lib.rs index ddca64a..89cdd7d 100644 --- a/apps/system/components/MlCoordinator/ml-common/src/lib.rs +++ b/apps/system/components/MlCoordinator/ml-common/src/lib.rs @@ -1,9 +1,10 @@ // TODO(jesionowski): What are the actual errors we may encounter? #[derive(Copy, Clone, Debug)] pub enum ExecutionError { + ModelNotLoaded, InvalidInstruction, InvalidFetch, - CoreReset + CoreReset, } // The abstraction layer over the "hardware" of running an execution. @@ -12,6 +13,15 @@ pub trait ExecutiveInterface { fn run_model(&self, model: &Model) -> Result<&[u8], ExecutionError>; } +// Options for execution that may be set by the application. +pub struct ModelOptions { + pub rate: u32, +} + +// Immutable model attributes. +#[derive(PartialEq, Debug)] pub struct Model { pub output_activations_len: usize, -} \ No newline at end of file +} + +pub const MAX_MODELS: u32 = 10; diff --git a/apps/system/components/MlCoordinator/fake-executive/Cargo.toml b/apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml similarity index 59% rename from apps/system/components/MlCoordinator/fake-executive/Cargo.toml rename to apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml index 3955d68..188a734 100644 --- a/apps/system/components/MlCoordinator/fake-executive/Cargo.toml +++ b/apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml @@ -1,8 +1,9 @@ [package] -name = "fake-executive" +name = "ml-coordinator" version = "0.1.0" authors = ["Adam Jesionowski "] edition = "2018" [dependencies] -ml-common = { path = "../ml-common" } \ No newline at end of file +ml-common = { path = "../ml-common" } +arrayvec = "0.5.2" \ No newline at end of file diff --git a/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs b/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs new file mode 100644 index 0000000..6b2548e --- /dev/null +++ b/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs @@ -0,0 +1,161 @@ +#![no_std] +use arrayvec::ArrayVec; +use ml_common as ml; +use ml_common::ExecutiveInterface; + +pub struct MlCoordinator<'a> { + executive: &'a dyn ExecutiveInterface, + // Application owns the model struct. + // ML Coordinator owns a reference to the model. + models: ArrayVec<[&'a ml::Model; 10]>, +} + +impl<'a> MlCoordinator<'a> { + // Create a new ML Coordinator instance. + fn new(executive: &'a dyn ExecutiveInterface) -> MlCoordinator<'a> { + let mut models = ArrayVec::<[&ml::Model; 10]>::new(); + + MlCoordinator { + executive: executive, + models: models, + } + } + + // Returns the index of the model if it has already been loaded. + fn has_model(&self, model: &'a ml::Model) -> Option { + self.models.iter().position(|&x| model == x) + } + + // Load the passed model. Returns true if the model was loaded + // successfully, and false if there is no room or the model was already loaded. + // This function will eventually perform validation of the model, create transfer buffers, etc. + #[must_use] + fn load(&mut self, model: &'a ml::Model) -> bool { + if self.models.is_full() || self.has_model(model).is_some() { + return false; + } + self.models.push(model); + true + } + + // Unload the passed model. Returns true if the model was unloaded successfully, + // false if the model was not loaded in the first place. + fn unload(&mut self, model: &'a ml::Model) -> bool { + let res = self.has_model(model); + if let Some(idx) = res { + self.models.remove(idx); + } + res.is_some() + } + + // Execute the passed model. + fn execute(&self, model: &'a ml::Model) -> Result<&'a [u8], ml::ExecutionError> { + self.has_model(model).ok_or(ml::ExecutionError::ModelNotLoaded)?; + self.executive.run_model(model) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct FakeExecutive { + output_memory: [u8; 512], + fake_error: Option, + } + + impl ExecutiveInterface for FakeExecutive { + fn run_model(&self, _model: &ml::Model) -> Result<&[u8], ml::ExecutionError> { + match self.fake_error { + Some(err) => Err(err), + None => Ok(&self.output_memory), + } + } + } + + #[test] + fn test_load_unload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert!(ml_coord.unload(&model)); + } + + #[test] + fn test_unload_noload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert_eq!(ml_coord.unload(&model), false); + } + + + #[test] + fn test_unload_twice() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert!(ml_coord.unload(&model)); + assert_eq!(ml_coord.unload(&model), false); + } + + #[test] + fn test_load_twice() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert_eq!(ml_coord.load(&model), false); + } + + #[test] + fn test_execute_noload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.execute(&model).is_err()); + } + + #[test] + fn test_execute() { + let exec = tests::FakeExecutive { + output_memory: [0xAD; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + + let res = ml_coord.execute(&model); + assert_eq!(res.unwrap()[0], 0xAD) + } +}