From 1e97658223690f4c5008264f56b341157523059a Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 25 Jun 2024 15:02:36 +0300 Subject: [PATCH 01/26] Hardcode rust version to 1.79 with compiles the code and passes all test --- flake.lock | 33 ++++++++++++++++++++++++--------- flake.nix | 4 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/flake.lock b/flake.lock index 99aca45bd6..4c26fadcc6 100644 --- a/flake.lock +++ b/flake.lock @@ -2,22 +2,21 @@ "nodes": { "fenix": { "inputs": { - "nixpkgs": [ - "nixpkgs" - ], + "nixpkgs": "nixpkgs", "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1716877613, - "narHash": "sha256-GOCKwLphClUGKw0gFDZZmF2UM3vLLLnWrFbAH2AINCI=", + "lastModified": 1710742993, + "narHash": "sha256-W0PQCe0bW3hKF5lHawXrKynBcdSP18Qa4sb8DcUfOqI=", "owner": "nix-community", "repo": "fenix", - "rev": "08ea8011dd25421c104a5f44d16a713a27d93fde", + "rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825", "type": "github" }, "original": { "owner": "nix-community", "repo": "fenix", + "rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825", "type": "github" } }, @@ -58,6 +57,22 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1719075281, + "narHash": "sha256-CyyxvOwFf12I91PBWz43iGT1kjsf5oi6ax7CrvaMyAo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a71e967ef3694799d0c418c98332f7ff4cc5f6af", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1703013332, "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", @@ -73,7 +88,7 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1681358109, "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", @@ -93,7 +108,7 @@ "inputs": { "fenix": "fenix", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay" } }, @@ -117,7 +132,7 @@ "rust-overlay": { "inputs": { "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1703124916, diff --git a/flake.nix b/flake.nix index 945e121720..8d2cc8ea26 100644 --- a/flake.nix +++ b/flake.nix @@ -7,8 +7,8 @@ flake-utils.url = "github:numtide/flake-utils"; fenix = { - url = "github:nix-community/fenix"; - inputs.nixpkgs.follows = "nixpkgs"; + url = "github:nix-community/fenix?rev=6f2fec850f569d61562d3a47dc263f19e9c7d825"; + #inputs.nixpkgs.follows = "nixpkgs"; }; }; From d83f92fd1308b11c7bed20dd296ff75a9197be41 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Thu, 30 May 2024 17:24:11 +0300 Subject: [PATCH 02/26] Say hello component works like a charm with this specific commands cd ~/code/repos/spin/crates/core && cd ./tests/core-wasi-test/ && cargo build && ls -l target/wasm32-wasi/debug/core-wasi-test.wasm && cp target/wasm32-wasi/debug/core-wasi-test.wasm /home/yordan/code/repos/spin/crates/core/../../target/test-programs/core-wasi-test.wasm && cd ../.. && cargo test --- crates/core/tests/core-wasi-test/src/main.rs | 27 +++++++--- crates/core/tests/core-wasi-test/wit/nn.wit | 7 +++ crates/core/tests/integration_test.rs | 56 ++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 crates/core/tests/core-wasi-test/wit/nn.wit diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index a1bc0ed28d..6a98437b6a 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -4,17 +4,27 @@ //! invalid argument(s). use std::time::Duration; - -wit_bindgen::generate!({ - world: "multiplier", - path: "wit/multiplier.wit" -}); +mod multiplier { + wit_bindgen::generate!({ + world: "multiplier", + path: "wit/multiplier.wit" + }); +} +mod nn { + wit_bindgen::generate!({ + world: "nn", + path: "wit/nn.wit" + }); +} +use crate::nn::hello::say_hello; type Result = std::result::Result<(), Box>; fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); + let output = "Gogo".to_string(); + match cmd.as_str() { "noop" => (), "echo" => { @@ -47,7 +57,12 @@ fn main() -> Result { "multiply" => { let input: i32 = args.next().expect("input").parse().expect("i32"); eprintln!("multiply {input}"); - let output = imports::multiply(input); + let output = multiplier::imports::multiply(input); + println!("{output}"); + } + "hello" => { + let input: String = args.next().expect("input").parse().expect("String"); + let output = say_hello(&input); println!("{output}"); } "sleep" => { diff --git a/crates/core/tests/core-wasi-test/wit/nn.wit b/crates/core/tests/core-wasi-test/wit/nn.wit new file mode 100644 index 0000000000..b993d979d5 --- /dev/null +++ b/crates/core/tests/core-wasi-test/wit/nn.wit @@ -0,0 +1,7 @@ +package test:test; + +world nn { + import hello: interface { + say-hello: func(x: string) -> string; + } +} diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 726f9aa596..539f4d7347 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -159,6 +159,28 @@ async fn test_host_component_data_update() { assert_eq!(stdout, "500"); } +#[tokio::test(flavor = "multi_thread")] +async fn test_host_component_nn() { + let engine = test_engine(); + let nn_handle = engine + .find_host_component_handle::() + .unwrap(); + + let stdout = run_core_wasi_test_engine( + &engine, + ["hello", "GGyci"], + |store_builder| { + store_builder + .host_components_data() + .set(nn_handle, NN{}); + }, + |_| {}, + ) + .await + .unwrap(); + assert_eq!(stdout, "Hello bace GGyci!"); +} + #[tokio::test(flavor = "multi_thread")] #[cfg(not(tarpaulin))] async fn test_panic() { @@ -178,6 +200,7 @@ fn test_config() -> Config { fn test_engine() -> Engine<()> { let mut builder = Engine::builder(&test_config()).unwrap(); builder.add_host_component(MultiplierHostComponent).unwrap(); + builder.add_host_component(NNHostComponent).unwrap(); builder .link_import(|l, _| wasmtime_wasi::add_to_linker_async(l)) .unwrap(); @@ -210,6 +233,7 @@ async fn run_core_wasi_test_engine<'a>( let mut store = store_builder.build()?; let module_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../target/test-programs/core-wasi-test.wasm"); + println!("module_path = {module_path:?}"); let component = spin_componentize::componentize_command(&fs::read(module_path).await?)?; let component = Component::new(engine.as_ref(), &component)?; let instance_pre = engine.instantiate_pre(&component)?; @@ -266,6 +290,38 @@ impl multiplier::imports::Host for Multiplier { } } + +#[derive(Clone)] +struct NNHostComponent; + +mod nn { + wasmtime::component::bindgen!("nn" in "tests/core-wasi-test/wit"); +} + +impl HostComponent for NNHostComponent { + type Data = NN; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + nn::hello::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + NN{} + } +} + +struct NN {} + +impl nn::hello::Host for NN { + fn say_hello(&mut self, x: String) -> wasmtime::Result { + Ok(format!("Hello bace {x}!")) + } +} + + // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); From 3820fd4fe0010067a6eb3313e64332cdd9504878 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Fri, 31 May 2024 10:26:19 +0300 Subject: [PATCH 03/26] Rename NN to hello --- crates/core/tests/core-wasi-test/src/main.rs | 8 +-- .../core-wasi-test/wit/{nn.wit => hello.wit} | 2 +- crates/core/tests/integration_test.rs | 51 ++++++++++--------- 3 files changed, 31 insertions(+), 30 deletions(-) rename crates/core/tests/core-wasi-test/wit/{nn.wit => hello.wit} (87%) diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 6a98437b6a..f5d3fb281c 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -10,13 +10,13 @@ mod multiplier { path: "wit/multiplier.wit" }); } -mod nn { +mod hello { wit_bindgen::generate!({ - world: "nn", - path: "wit/nn.wit" + world: "hello", + path: "wit/hello.wit" }); } -use crate::nn::hello::say_hello; +use crate::hello::hello::say_hello; type Result = std::result::Result<(), Box>; diff --git a/crates/core/tests/core-wasi-test/wit/nn.wit b/crates/core/tests/core-wasi-test/wit/hello.wit similarity index 87% rename from crates/core/tests/core-wasi-test/wit/nn.wit rename to crates/core/tests/core-wasi-test/wit/hello.wit index b993d979d5..f6944c7779 100644 --- a/crates/core/tests/core-wasi-test/wit/nn.wit +++ b/crates/core/tests/core-wasi-test/wit/hello.wit @@ -1,6 +1,6 @@ package test:test; -world nn { +world hello { import hello: interface { say-hello: func(x: string) -> string; } diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 539f4d7347..a50f528833 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,6 +4,7 @@ use std::{ time::{Duration, Instant}, }; +use crate::hello::{HelloHostComponent, HelloHostImpl}; use anyhow::Context; use spin_core::{ Component, Config, Engine, HostComponent, I32Exit, Store, StoreBuilder, Trap, WasiVersion, @@ -162,8 +163,8 @@ async fn test_host_component_data_update() { #[tokio::test(flavor = "multi_thread")] async fn test_host_component_nn() { let engine = test_engine(); - let nn_handle = engine - .find_host_component_handle::() + let hello_handle = engine + .find_host_component_handle::() .unwrap(); let stdout = run_core_wasi_test_engine( @@ -172,7 +173,7 @@ async fn test_host_component_nn() { |store_builder| { store_builder .host_components_data() - .set(nn_handle, NN{}); + .set(hello_handle, HelloHostImpl {}); }, |_| {}, ) @@ -200,7 +201,7 @@ fn test_config() -> Config { fn test_engine() -> Engine<()> { let mut builder = Engine::builder(&test_config()).unwrap(); builder.add_host_component(MultiplierHostComponent).unwrap(); - builder.add_host_component(NNHostComponent).unwrap(); + builder.add_host_component(HelloHostComponent).unwrap(); builder .link_import(|l, _| wasmtime_wasi::add_to_linker_async(l)) .unwrap(); @@ -290,38 +291,38 @@ impl multiplier::imports::Host for Multiplier { } } +mod hello { + wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); -#[derive(Clone)] -struct NNHostComponent; + use spin_core::HostComponent; -mod nn { - wasmtime::component::bindgen!("nn" in "tests/core-wasi-test/wit"); -} + #[derive(Clone)] + pub struct HelloHostComponent; -impl HostComponent for NNHostComponent { - type Data = NN; + impl HostComponent for HelloHostComponent { + type Data = HelloHostImpl; - fn add_to_linker( - linker: &mut spin_core::Linker, - get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - nn::hello::add_to_linker(linker, get) - } + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + hello::add_to_linker(linker, get) + } - fn build_data(&self) -> Self::Data { - NN{} + fn build_data(&self) -> Self::Data { + HelloHostImpl {} + } } -} -struct NN {} + pub struct HelloHostImpl {} -impl nn::hello::Host for NN { - fn say_hello(&mut self, x: String) -> wasmtime::Result { - Ok(format!("Hello bace {x}!")) + impl hello::Host for HelloHostImpl { + fn say_hello(&mut self, x: String) -> wasmtime::Result { + Ok(format!("Hello bace {x}!")) + } } } - // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); From 62369160b1c82816c8277a9242694f9eec836f56 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Fri, 31 May 2024 12:52:10 +0300 Subject: [PATCH 04/26] All test pass - golden --- crates/core/tests/hello_component.rs | 32 ++++++++++++++++++++++ crates/core/tests/integration_test.rs | 38 ++++----------------------- 2 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 crates/core/tests/hello_component.rs diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs new file mode 100644 index 0000000000..0b4f58769b --- /dev/null +++ b/crates/core/tests/hello_component.rs @@ -0,0 +1,32 @@ + +pub mod hello { + wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); + + use spin_core::HostComponent; + + #[derive(Clone)] + pub struct HelloHostComponent; + + impl HostComponent for HelloHostComponent { + type Data = HelloHostImpl; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + hello::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + HelloHostImpl {} + } + } + + pub struct HelloHostImpl {} + + impl hello::Host for HelloHostImpl { + fn say_hello(&mut self, x: String) -> wasmtime::Result { + Ok(format!("Hello bace {x}!")) + } + } +} \ No newline at end of file diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index a50f528833..e2feeb2cce 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,7 +4,11 @@ use std::{ time::{Duration, Instant}, }; -use crate::hello::{HelloHostComponent, HelloHostImpl}; + +mod hello_component; + +use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; + use anyhow::Context; use spin_core::{ Component, Config, Engine, HostComponent, I32Exit, Store, StoreBuilder, Trap, WasiVersion, @@ -291,38 +295,6 @@ impl multiplier::imports::Host for Multiplier { } } -mod hello { - wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); - - use spin_core::HostComponent; - - #[derive(Clone)] - pub struct HelloHostComponent; - - impl HostComponent for HelloHostComponent { - type Data = HelloHostImpl; - - fn add_to_linker( - linker: &mut spin_core::Linker, - get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - hello::add_to_linker(linker, get) - } - - fn build_data(&self) -> Self::Data { - HelloHostImpl {} - } - } - - pub struct HelloHostImpl {} - - impl hello::Host for HelloHostImpl { - fn say_hello(&mut self, x: String) -> wasmtime::Result { - Ok(format!("Hello bace {x}!")) - } - } -} - // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); From 2ed57f8ac61f973b5289d4c03935c5e41bbdf199 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 3 Jun 2024 14:50:43 +0300 Subject: [PATCH 05/26] WIT import interface in same file - WORKS ! --- crates/core/tests/core-wasi-test/src/main.rs | 4 +- .../core/tests/core-wasi-test/wit/hello.wit | 9 +- crates/core/tests/core-wasi-test/wit/ml.wit | 168 ++++++++++++++++++ crates/core/tests/hello_component.rs | 9 +- crates/core/tests/integration_test.rs | 1 + crates/core/tests/ml.rs | 70 ++++++++ 6 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 crates/core/tests/core-wasi-test/wit/ml.wit create mode 100644 crates/core/tests/ml.rs diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index f5d3fb281c..8f460ea3f9 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -16,14 +16,14 @@ mod hello { path: "wit/hello.wit" }); } -use crate::hello::hello::say_hello; + +use crate::hello::test::test::gggg2::say_hello; type Result = std::result::Result<(), Box>; fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); - let output = "Gogo".to_string(); match cmd.as_str() { "noop" => (), diff --git a/crates/core/tests/core-wasi-test/wit/hello.wit b/crates/core/tests/core-wasi-test/wit/hello.wit index f6944c7779..405a695e64 100644 --- a/crates/core/tests/core-wasi-test/wit/hello.wit +++ b/crates/core/tests/core-wasi-test/wit/hello.wit @@ -1,7 +1,10 @@ package test:test; world hello { - import hello: interface { - say-hello: func(x: string) -> string; - } + import gggg2; } + +interface gggg2 { + say-hello: func(x: string) -> string; +} + diff --git a/crates/core/tests/core-wasi-test/wit/ml.wit b/crates/core/tests/core-wasi-test/wit/ml.wit new file mode 100644 index 0000000000..953e58d09c --- /dev/null +++ b/crates/core/tests/core-wasi-test/wit/ml.wit @@ -0,0 +1,168 @@ +package test:test; + +/// `wasi-nn` is a WASI API for performing machine learning (ML) inference. The API is not (yet) +/// capable of performing ML training. WebAssembly programs that want to use a host's ML +/// capabilities can access these capabilities through `wasi-nn`'s core abstractions: _graphs_ and +/// _tensors_. A user `load`s an ML model -- instantiated as a _graph_ -- to use in an ML _backend_. +/// Then, the user passes _tensor_ inputs to the _graph_, computes the inference, and retrieves the +/// _tensor_ outputs. +/// +/// This example world shows how to use these primitives together. +world ml { + import tensor; + import graph; + import inference; + import errors; +} + +/// All inputs and outputs to an ML inference are represented as `tensor`s. +interface tensor { + /// The dimensions of a tensor. + /// + /// The array length matches the tensor rank and each element in the array describes the size of + /// each dimension + type tensor-dimensions = list; + + /// The type of the elements in a tensor. + enum tensor-type { + FP16, + FP32, + FP64, + BF16, + U8, + I32, + I64 + } + + /// The tensor data. + /// + /// Initially conceived as a sparse representation, each empty cell would be filled with zeros + /// and the array length must match the product of all of the dimensions and the number of bytes + /// in the type (e.g., a 2x2 tensor with 4-byte f32 elements would have a data array of length + /// 16). Naturally, this representation requires some knowledge of how to lay out data in + /// memory--e.g., using row-major ordering--and could perhaps be improved. + type tensor-data = list; + + resource tensor { + constructor(dimensions: tensor-dimensions, ty: tensor-type, data: tensor-data); + + // Describe the size of the tensor (e.g., 2x2x2x2 -> [2, 2, 2, 2]). To represent a tensor + // containing a single value, use `[1]` for the tensor dimensions. + dimensions: func() -> tensor-dimensions; + + // Describe the type of element in the tensor (e.g., `f32`). + ty: func() -> tensor-type; + + // Return the tensor data. + data: func() -> tensor-data; + } +} + +/// A `graph` is a loaded instance of a specific ML model (e.g., MobileNet) for a specific ML +/// framework (e.g., TensorFlow): +interface graph { + use errors.{error}; + use tensor.{tensor}; + use inference.{graph-execution-context}; + + /// An execution graph for performing inference (i.e., a model). + resource graph { + init-execution-context: func() -> result; + } + + /// Describes the encoding of the graph. This allows the API to be implemented by various + /// backends that encode (i.e., serialize) their graph IR with different formats. + enum graph-encoding { + openvino, + onnx, + tensorflow, + pytorch, + tensorflowlite, + ggml, + autodetect, + } + + /// Define where the graph should be executed. + enum execution-target { + cpu, + gpu, + tpu + } + + /// The graph initialization data. + /// + /// This gets bundled up into an array of buffers because implementing backends may encode their + /// graph IR in parts (e.g., OpenVINO stores its IR and weights separately). + type graph-builder = list; + + /// Load a `graph` from an opaque sequence of bytes to use for inference. + load: func(builder: list, encoding: graph-encoding, target: execution-target) -> result; + + /// Load a `graph` by name. + /// + /// How the host expects the names to be passed and how it stores the graphs for retrieval via + /// this function is **implementation-specific**. This allows hosts to choose name schemes that + /// range from simple to complex (e.g., URLs?) and caching mechanisms of various kinds. + load-by-name: func(name: string) -> result; +} + +/// An inference "session" is encapsulated by a `graph-execution-context`. This structure binds a +/// `graph` to input tensors before `compute`-ing an inference: +interface inference { + use errors.{error}; + use tensor.{tensor, tensor-data}; + + /// Bind a `graph` to the input and output tensors for an inference. + /// + /// TODO: this may no longer be necessary in WIT + /// (https://github.com/WebAssembly/wasi-nn/issues/43) + resource graph-execution-context { + /// Define the inputs to use for inference. + set-input: func(name: string, tensor: tensor) -> result<_, error>; + + /// Compute the inference on the given inputs. + /// + /// Note the expected sequence of calls: `set-input`, `compute`, `get-output`. TODO: this + /// expectation could be removed as a part of + /// https://github.com/WebAssembly/wasi-nn/issues/43. + compute: func() -> result<_, error>; + + /// Extract the outputs after inference. + get-output: func(name: string) -> result; + } +} + +/// TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42) +interface errors { + enum error-code { + // Caller module passed an invalid argument. + invalid-argument, + // Invalid encoding. + invalid-encoding, + // The operation timed out. + timeout, + // Runtime Error. + runtime-error, + // Unsupported operation. + unsupported-operation, + // Graph is too large. + too-large, + // Graph not found. + not-found, + // The operation is insecure or has insufficient privilege to be performed. + // e.g., cannot access a hardware feature requested + security, + // The operation failed for an unspecified reason. + unknown + } + + resource error { + constructor(code: error-code, data: string); + + /// Return the error code. + code: func() -> error-code; + + /// Errors can propagated with backend specific status through a string value. + data: func() -> string; + } +} diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs index 0b4f58769b..646a4467dc 100644 --- a/crates/core/tests/hello_component.rs +++ b/crates/core/tests/hello_component.rs @@ -1,8 +1,12 @@ +//use crate::hello_component::hello::test::test::gggg2; +//use crate::hello_component::gggg2; + pub mod hello { wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; + use test::test::gggg2; #[derive(Clone)] pub struct HelloHostComponent; @@ -14,7 +18,7 @@ pub mod hello { linker: &mut spin_core::Linker, get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, ) -> anyhow::Result<()> { - hello::add_to_linker(linker, get) + Hello::add_to_linker(linker, get) } fn build_data(&self) -> Self::Data { @@ -24,9 +28,10 @@ pub mod hello { pub struct HelloHostImpl {} - impl hello::Host for HelloHostImpl { + impl gggg2::Host for HelloHostImpl { fn say_hello(&mut self, x: String) -> wasmtime::Result { Ok(format!("Hello bace {x}!")) } } + } \ No newline at end of file diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index e2feeb2cce..9158968de4 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -6,6 +6,7 @@ use std::{ mod hello_component; +//mod ml; use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; diff --git a/crates/core/tests/ml.rs b/crates/core/tests/ml.rs new file mode 100644 index 0000000000..5f3f7f1a4a --- /dev/null +++ b/crates/core/tests/ml.rs @@ -0,0 +1,70 @@ +/*use crate::ml::Ml::test::test::graph::Host as GraphHost; +use crate::ml::Ml::test::test::graph::GraphBuilder; +use crate::ml::Ml::test::test::graph::GraphEncoding; +use crate::ml::Ml::test::test::graph::ExecutionTarget; +use crate::ml::Ml::test::test::graph::Graph;// as GraphGraph; + +use crate::ml::Ml::test::test::errors::Error; + + +pub mod Ml { + wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); + + use spin_core::HostComponent; + + //use crate::nn_prototype::nn_prototype::test::test::errors::Host as ErrorHost; + //use crate::nn_prototype::nn_prototype::test::test::graph::Host as GraphHost; + //use crate::nn_prototype::nn_prototype::test::test::errors::ErrorCode as ErrorCode; + + + + use crate::ml::GraphHost; + use crate::ml::GraphBuilder; + use crate::ml::GraphEncoding; + use crate::ml::ExecutionTarget; + use crate::ml::Graph; + use crate::ml::Error; + + + #[derive(Clone)] + pub struct NNHostComponent; + + impl HostComponent for NNHostComponent { + type Data = MLHostImpl; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + Ml::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + MLHostImpl {} + } + } + + pub struct MLHostImpl {} + + impl GraphHost for MLHostImpl { + fn load(builder: &GraphBuilder, + encoding: &GraphEncoding, + target: &ExecutionTarget) -> wasmtime::Result { + + } + //load: func(builder: list, encoding: graph-encoding, target: execution-target) -> result; + } + + //impl Ml::error::ErrorHost for MLHostImpl { + + //} + /*impl ErrorHost for MLHostImpl { + fn code(&mut self) -> wasmtime::Result { + Ok(self.code) + } + /*fn say_hello(&mut self, x: String) -> wasmtime::Result { + Ok(format!("Hello bace {x}!")) + }*/ + } + */ +}*/ \ No newline at end of file From cf313f5f1ae6f32c22850a35a73ac64be0925b68 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 3 Jun 2024 16:25:42 +0300 Subject: [PATCH 06/26] Errors(kotenca) interface signatures match --- .../core/tests/core-wasi-test/wit/hello.wit | 35 ++++++++++++++++++ crates/core/tests/hello_component.rs | 36 ++++++++++++++++--- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/crates/core/tests/core-wasi-test/wit/hello.wit b/crates/core/tests/core-wasi-test/wit/hello.wit index 405a695e64..3a9ce5bd39 100644 --- a/crates/core/tests/core-wasi-test/wit/hello.wit +++ b/crates/core/tests/core-wasi-test/wit/hello.wit @@ -2,9 +2,44 @@ package test:test; world hello { import gggg2; + import kotenca; } interface gggg2 { say-hello: func(x: string) -> string; } +/// TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42) +interface kotenca { + enum error-code { + // Caller module passed an invalid argument. + invalid-argument, + // Invalid encoding. + invalid-encoding, + // The operation timed out. + timeout, + // Runtime Error. + runtime-error, + // Unsupported operation. + unsupported-operation, + // Graph is too large. + too-large, + // Graph not found. + not-found, + // The operation is insecure or has insufficient privilege to be performed. + // e.g., cannot access a hardware feature requested + security, + // The operation failed for an unspecified reason. + unknown + } + + resource error { + constructor(code: error-code, data: string); + + /// Return the error code. + code: func() -> error-code; + + /// Errors can propagated with backend specific status through a string value. + data: func() -> string; + } +} diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs index 646a4467dc..e191421cef 100644 --- a/crates/core/tests/hello_component.rs +++ b/crates/core/tests/hello_component.rs @@ -1,12 +1,12 @@ - -//use crate::hello_component::hello::test::test::gggg2; -//use crate::hello_component::gggg2; - pub mod hello { wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; + use anyhow::anyhow; use test::test::gggg2; + use test::test::kotenca; + use test::test::kotenca::{ErrorCode}; + #[derive(Clone)] pub struct HelloHostComponent; @@ -34,4 +34,32 @@ pub mod hello { } } + impl kotenca::HostError for HelloHostImpl { + fn new(&mut self, code: kotenca::ErrorCode, data: String) -> Result, anyhow::Error> { + //Ok(kotenca::Error(code, data)) + Err(anyhow!("Not implemented")) + } + + fn drop(&mut self, val: wasmtime::component::Resource) -> Result<(), anyhow::Error>{ + Ok(()) + } + + fn code(&mut self, val: wasmtime::component::Resource) -> Result { + println!("{:?}", &val); + //Ok(val.code) + Err(anyhow!("Not implemented")) + } + + fn data(&mut self, val: wasmtime::component::Resource) -> Result { + println!("{:?}", &val); + + //Ok(val.data) + Err(anyhow!("Not implemented")) + } + + } + + impl kotenca::Host for HelloHostImpl {} + + } \ No newline at end of file From bddabbf7c53db22e89eef85607d7049eda62a256 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 4 Jun 2024 11:46:43 +0300 Subject: [PATCH 07/26] All trait signatures match and the code compiles --- .../core/tests/core-wasi-test/wit/hello.wit | 36 ---- crates/core/tests/hello_component.rs | 38 +---- crates/core/tests/integration_test.rs | 3 +- crates/core/tests/ml.rs | 157 +++++++++++++----- 4 files changed, 125 insertions(+), 109 deletions(-) diff --git a/crates/core/tests/core-wasi-test/wit/hello.wit b/crates/core/tests/core-wasi-test/wit/hello.wit index 3a9ce5bd39..2fb999f481 100644 --- a/crates/core/tests/core-wasi-test/wit/hello.wit +++ b/crates/core/tests/core-wasi-test/wit/hello.wit @@ -2,44 +2,8 @@ package test:test; world hello { import gggg2; - import kotenca; } interface gggg2 { say-hello: func(x: string) -> string; } - -/// TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42) -interface kotenca { - enum error-code { - // Caller module passed an invalid argument. - invalid-argument, - // Invalid encoding. - invalid-encoding, - // The operation timed out. - timeout, - // Runtime Error. - runtime-error, - // Unsupported operation. - unsupported-operation, - // Graph is too large. - too-large, - // Graph not found. - not-found, - // The operation is insecure or has insufficient privilege to be performed. - // e.g., cannot access a hardware feature requested - security, - // The operation failed for an unspecified reason. - unknown - } - - resource error { - constructor(code: error-code, data: string); - - /// Return the error code. - code: func() -> error-code; - - /// Errors can propagated with backend specific status through a string value. - data: func() -> string; - } -} diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs index e191421cef..21d0dda87c 100644 --- a/crates/core/tests/hello_component.rs +++ b/crates/core/tests/hello_component.rs @@ -2,11 +2,10 @@ pub mod hello { wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; - use anyhow::anyhow; + //use anyhow::anyhow; use test::test::gggg2; - use test::test::kotenca; - use test::test::kotenca::{ErrorCode}; - + //use test::test::errors; + //use test::test::errors::{ErrorCode}; #[derive(Clone)] pub struct HelloHostComponent; @@ -33,33 +32,4 @@ pub mod hello { Ok(format!("Hello bace {x}!")) } } - - impl kotenca::HostError for HelloHostImpl { - fn new(&mut self, code: kotenca::ErrorCode, data: String) -> Result, anyhow::Error> { - //Ok(kotenca::Error(code, data)) - Err(anyhow!("Not implemented")) - } - - fn drop(&mut self, val: wasmtime::component::Resource) -> Result<(), anyhow::Error>{ - Ok(()) - } - - fn code(&mut self, val: wasmtime::component::Resource) -> Result { - println!("{:?}", &val); - //Ok(val.code) - Err(anyhow!("Not implemented")) - } - - fn data(&mut self, val: wasmtime::component::Resource) -> Result { - println!("{:?}", &val); - - //Ok(val.data) - Err(anyhow!("Not implemented")) - } - - } - - impl kotenca::Host for HelloHostImpl {} - - -} \ No newline at end of file +} diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 9158968de4..9a5822b0dd 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,9 +4,8 @@ use std::{ time::{Duration, Instant}, }; - mod hello_component; -//mod ml; +mod ml; use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; diff --git a/crates/core/tests/ml.rs b/crates/core/tests/ml.rs index 5f3f7f1a4a..17601c4b47 100644 --- a/crates/core/tests/ml.rs +++ b/crates/core/tests/ml.rs @@ -1,30 +1,18 @@ -/*use crate::ml::Ml::test::test::graph::Host as GraphHost; -use crate::ml::Ml::test::test::graph::GraphBuilder; -use crate::ml::Ml::test::test::graph::GraphEncoding; -use crate::ml::Ml::test::test::graph::ExecutionTarget; -use crate::ml::Ml::test::test::graph::Graph;// as GraphGraph; - -use crate::ml::Ml::test::test::errors::Error; - - pub mod Ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; - //use crate::nn_prototype::nn_prototype::test::test::errors::Host as ErrorHost; - //use crate::nn_prototype::nn_prototype::test::test::graph::Host as GraphHost; - //use crate::nn_prototype::nn_prototype::test::test::errors::ErrorCode as ErrorCode; - + use anyhow::anyhow; + use test::test::errors; + use test::test::graph; + use test::test::inference; + use test::test::tensor; - - use crate::ml::GraphHost; - use crate::ml::GraphBuilder; - use crate::ml::GraphEncoding; - use crate::ml::ExecutionTarget; - use crate::ml::Graph; - use crate::ml::Error; - + use test::test::errors::ErrorCode; + use test::test::graph::{Error, ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; + use test::test::inference::GraphExecutionContext; + use wasmtime::component::Resource; #[derive(Clone)] pub struct NNHostComponent; @@ -46,25 +34,120 @@ pub mod Ml { pub struct MLHostImpl {} - impl GraphHost for MLHostImpl { - fn load(builder: &GraphBuilder, - encoding: &GraphEncoding, - target: &ExecutionTarget) -> wasmtime::Result { - + impl graph::HostGraph for MLHostImpl { + fn init_execution_context( + &mut self, + graph: Resource, + ) -> Result< + Result, Resource>, + anyhow::Error, + > { + Err(anyhow!("Not implemented")) + } + fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { + Err(anyhow!("Not implemented")) } - //load: func(builder: list, encoding: graph-encoding, target: execution-target) -> result; } - //impl Ml::error::ErrorHost for MLHostImpl { + impl errors::HostError for MLHostImpl { + fn new( + &mut self, + code: errors::ErrorCode, + data: String, + ) -> Result, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + + fn drop(&mut self, err: Resource) -> Result<(), anyhow::Error> { + Err(anyhow!("Not implemented")) + } + + fn code(&mut self, err: Resource) -> Result { + Err(anyhow!("Not implemented")) + } - //} - /*impl ErrorHost for MLHostImpl { - fn code(&mut self) -> wasmtime::Result { - Ok(self.code) + fn data(&mut self, err: Resource) -> Result { + Err(anyhow!("Not implemented")) } - /*fn say_hello(&mut self, x: String) -> wasmtime::Result { - Ok(format!("Hello bace {x}!")) - }*/ } - */ -}*/ \ No newline at end of file + impl tensor::HostTensor for MLHostImpl { + fn new( + &mut self, + tensor_dimentions: Vec, + tensor_type: tensor::TensorType, + tensor_data: Vec, + ) -> Result, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn dimensions( + &mut self, + tensor: Resource, + ) -> Result, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn ty( + &mut self, + tensor: Resource, + ) -> Result { + Err(anyhow!("Not implemented")) + } + fn data(&mut self, tensor: Resource) -> Result, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { + Err(anyhow!("Not implemented")) + } + } + + impl inference::HostGraphExecutionContext for MLHostImpl { + fn set_input( + &mut self, + graph_execution_context: Resource, + name: String, + val2: Resource, + ) -> Result>, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn compute( + &mut self, + graph_execution_context: Resource, + ) -> Result>, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn get_output( + &mut self, + graph_execution_context: Resource, + name: String, + ) -> Result, Resource>, anyhow::Error> + { + Err(anyhow!("Not implemented")) + } + fn drop( + &mut self, + graph_execution_context: Resource, + ) -> Result<(), anyhow::Error> { + Err(anyhow!("Not implemented")) + } + } + + impl errors::Host for MLHostImpl {} + impl graph::Host for MLHostImpl { + fn load( + &mut self, + graph: Vec>, + graph_encoding: GraphEncoding, + target: ExecutionTarget, + ) -> Result, Resource>, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + fn load_by_name( + &mut self, + graph: String, + ) -> Result, Resource>, anyhow::Error> { + Err(anyhow!("Not implemented")) + } + } + + impl inference::Host for MLHostImpl {} + impl tensor::Host for MLHostImpl {} +} From 48f1414bb89a1df9f0bba523478617aaf98e8bd2 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 4 Jun 2024 13:21:13 +0300 Subject: [PATCH 08/26] Remove warnings --- crates/core/tests/core-wasi-test/src/main.rs | 11 +++++ crates/core/tests/hello_component.rs | 3 -- crates/core/tests/ml.rs | 52 ++++++++++---------- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 8f460ea3f9..796293035b 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -17,6 +17,14 @@ mod hello { }); } +mod ml { + wit_bindgen::generate!({ + world: "ml", + path: "wit/ml.wit" + }); +} + + use crate::hello::test::test::gggg2::say_hello; type Result = std::result::Result<(), Box>; @@ -65,6 +73,9 @@ fn main() -> Result { let output = say_hello(&input); println!("{output}"); } + "imagenet" => { + println!("Kuku!"); + } "sleep" => { let duration = Duration::from_millis(args.next().expect("duration_ms").parse().expect("u64")); diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs index 21d0dda87c..4abca99b75 100644 --- a/crates/core/tests/hello_component.rs +++ b/crates/core/tests/hello_component.rs @@ -2,10 +2,7 @@ pub mod hello { wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; - //use anyhow::anyhow; use test::test::gggg2; - //use test::test::errors; - //use test::test::errors::{ErrorCode}; #[derive(Clone)] pub struct HelloHostComponent; diff --git a/crates/core/tests/ml.rs b/crates/core/tests/ml.rs index 17601c4b47..4302c13352 100644 --- a/crates/core/tests/ml.rs +++ b/crates/core/tests/ml.rs @@ -1,4 +1,4 @@ -pub mod Ml { +pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); use spin_core::HostComponent; @@ -37,14 +37,14 @@ pub mod Ml { impl graph::HostGraph for MLHostImpl { fn init_execution_context( &mut self, - graph: Resource, + _graph: Resource, ) -> Result< Result, Resource>, anyhow::Error, > { Err(anyhow!("Not implemented")) } - fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { + fn drop(&mut self, _graph: Resource) -> Result<(), anyhow::Error> { Err(anyhow!("Not implemented")) } } @@ -52,49 +52,49 @@ pub mod Ml { impl errors::HostError for MLHostImpl { fn new( &mut self, - code: errors::ErrorCode, - data: String, + _code: errors::ErrorCode, + _data: String, ) -> Result, anyhow::Error> { Err(anyhow!("Not implemented")) } - fn drop(&mut self, err: Resource) -> Result<(), anyhow::Error> { + fn drop(&mut self, _error: Resource) -> Result<(), anyhow::Error> { Err(anyhow!("Not implemented")) } - fn code(&mut self, err: Resource) -> Result { + fn code(&mut self, _error: Resource) -> Result { Err(anyhow!("Not implemented")) } - fn data(&mut self, err: Resource) -> Result { + fn data(&mut self, _error: Resource) -> Result { Err(anyhow!("Not implemented")) } } impl tensor::HostTensor for MLHostImpl { fn new( &mut self, - tensor_dimentions: Vec, - tensor_type: tensor::TensorType, - tensor_data: Vec, + _tensor_dimentions: tensor::TensorDimensions, + _tensor_type: tensor::TensorType, + _tensor_data: tensor::TensorData, ) -> Result, anyhow::Error> { Err(anyhow!("Not implemented")) } fn dimensions( &mut self, - tensor: Resource, + _tensor: Resource, ) -> Result, anyhow::Error> { Err(anyhow!("Not implemented")) } fn ty( &mut self, - tensor: Resource, + _tensor: Resource, ) -> Result { Err(anyhow!("Not implemented")) } - fn data(&mut self, tensor: Resource) -> Result, anyhow::Error> { + fn data(&mut self, _tensor: Resource) -> Result { Err(anyhow!("Not implemented")) } - fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { + fn drop(&mut self, _tensor: Resource) -> Result<(), anyhow::Error> { Err(anyhow!("Not implemented")) } } @@ -102,29 +102,29 @@ pub mod Ml { impl inference::HostGraphExecutionContext for MLHostImpl { fn set_input( &mut self, - graph_execution_context: Resource, - name: String, - val2: Resource, + _graph_execution_context: Resource, + _input_name: String, + _tensor: Resource, ) -> Result>, anyhow::Error> { Err(anyhow!("Not implemented")) } fn compute( &mut self, - graph_execution_context: Resource, + _graph_execution_context: Resource, ) -> Result>, anyhow::Error> { Err(anyhow!("Not implemented")) } fn get_output( &mut self, - graph_execution_context: Resource, - name: String, + _graph_execution_context: Resource, + _input_name: String, ) -> Result, Resource>, anyhow::Error> { Err(anyhow!("Not implemented")) } fn drop( &mut self, - graph_execution_context: Resource, + _graph_execution_context: Resource, ) -> Result<(), anyhow::Error> { Err(anyhow!("Not implemented")) } @@ -134,15 +134,15 @@ pub mod Ml { impl graph::Host for MLHostImpl { fn load( &mut self, - graph: Vec>, - graph_encoding: GraphEncoding, - target: ExecutionTarget, + _graph: Vec, + _graph_encoding: GraphEncoding, + _target: ExecutionTarget, ) -> Result, Resource>, anyhow::Error> { Err(anyhow!("Not implemented")) } fn load_by_name( &mut self, - graph: String, + _graph: String, ) -> Result, Resource>, anyhow::Error> { Err(anyhow!("Not implemented")) } From 81e0f81d5e3de98f71c795dfc89cb7651b44acaf Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 4 Jun 2024 17:00:17 +0300 Subject: [PATCH 09/26] Example use of unimplemented ml component --- Cargo.lock | 34 ++++++++++++++++++ crates/core/Cargo.toml | 1 + crates/core/tests/core-wasi-test/src/main.rs | 36 +++++++++++++++++++- crates/core/tests/integration_test.rs | 29 +++++++++++++++- crates/core/tests/{ml.rs => ml_component.rs} | 7 ++-- 5 files changed, 102 insertions(+), 5 deletions(-) rename crates/core/tests/{ml.rs => ml_component.rs} (96%) diff --git a/Cargo.lock b/Cargo.lock index a25ff05871..2735eaeecc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5207,6 +5207,39 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "openvino" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24bd3a7ef39968e6a4f1b1206c1c876f9bd50cf739ccbcd69f8539bbac5dcc7a" +dependencies = [ + "openvino-finder", + "openvino-sys", + "thiserror", +] + +[[package]] +name = "openvino-finder" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d234d1394a413ea8adaf0c40806b9ad1946be6310b441f688840654a331973" +dependencies = [ + "cfg-if", + "log", +] + +[[package]] +name = "openvino-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c98acf37fc84ad9d7da4dc6c18f0f60ad209b43a6f555be01f9003d0a2a43d" +dependencies = [ + "env_logger", + "libloading", + "once_cell", + "openvino-finder", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -7491,6 +7524,7 @@ dependencies = [ "futures", "http 1.1.0", "io-extras", + "openvino", "rustix 0.37.27", "spin-componentize", "spin-telemetry", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index cba62cd50e..c6e18e8a5c 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -20,6 +20,7 @@ tokio = "1.0" bytes = "1.0" spin-telemetry = { path = "../telemetry" } http = "1.0" +openvino = { version = "0.6.0", features = ["runtime-linking",], optional = true } [target.'cfg(unix)'.dependencies] rustix = "0.37.19" diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 796293035b..2dca577810 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -23,7 +23,9 @@ mod ml { path: "wit/ml.wit" }); } - +use crate::ml::test::test::graph::ExecutionTarget; +use crate::ml::test::test::graph::GraphEncoding; +use std::path::Path; use crate::hello::test::test::gggg2::say_hello; @@ -74,6 +76,38 @@ fn main() -> Result { println!("{output}"); } "imagenet" => { + let path_as_string = args.next().expect("path"); + let path = Path::new(&path_as_string); + let model: Vec = std::fs::read(&path.join("model.xml"))?; + println!("Loaded model from xml len = {}", model.len()); + let weights = std::fs::read(&path.join("model.bin"))?; + println!("Loaded weigths with len = {}", weights.len()); + let imagenet_graph = ml::test::test::graph::load(&[model, weights], GraphEncoding::Openvino, ExecutionTarget::Cpu).unwrap(); + println!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); + let context = ml::test::test::graph::Graph::init_execution_context(&imagenet_graph).unwrap(); + println!("Created context with ID: {:?}", context); + + let image_data = std::fs::read("images/0.jpg").unwrap(); + + let tensor_data = image_data; // !! TODO !!convert image to tensor + + + let tensor_dimensions:Vec = vec![1, 3, 224, 224]; + let tensor_type = ml::test::test::tensor::TensorType::Fp32; + let tensor_id = ml::test::test::tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); + println!("Created tensor with ID: {:?}", tensor_id); + + let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0.jpg", tensor_id).unwrap(); + println!("Input set with ID: {:?}", set_input_result); + + let infered_result = ml::test::test::inference::GraphExecutionContext::compute(&context).unwrap(); + println!("Executed graph inference"); + let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0.jpg").unwrap(); + + let output_result = ml::test::test::tensor::Tensor::data(&output_result_id); + println!("output = {:?}", &output_result); + + println!("Kuku!"); } "sleep" => { diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 9a5822b0dd..8322e85847 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -5,9 +5,10 @@ use std::{ }; mod hello_component; -mod ml; +mod ml_component; use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; +use crate::ml_component::ml::{MLHostComponent, MLHostImpl}; use anyhow::Context; use spin_core::{ @@ -186,6 +187,30 @@ async fn test_host_component_nn() { assert_eq!(stdout, "Hello bace GGyci!"); } +#[tokio::test(flavor = "multi_thread")] +async fn test_host_component_imagenet() { + let engine = test_engine(); + let handle = engine + .find_host_component_handle::() + .unwrap(); + let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../target/test-programs/imagenet"); + println!("imagenet_path = {imagenet_path:?}"); + + let stdout = run_core_wasi_test_engine( + &engine, + ["imagenet", "/"], + |store_builder| { + store_builder.read_only_preopened_dir(&imagenet_path, "/".into()).unwrap(); + store_builder.host_components_data().set(handle, MLHostImpl {}); + }, + |_| {}, + ) + .await + .unwrap(); + assert_eq!(stdout, "Hello bace GGyci!"); +} + #[tokio::test(flavor = "multi_thread")] #[cfg(not(tarpaulin))] async fn test_panic() { @@ -206,6 +231,8 @@ fn test_engine() -> Engine<()> { let mut builder = Engine::builder(&test_config()).unwrap(); builder.add_host_component(MultiplierHostComponent).unwrap(); builder.add_host_component(HelloHostComponent).unwrap(); + builder.add_host_component(MLHostComponent).unwrap(); + builder .link_import(|l, _| wasmtime_wasi::add_to_linker_async(l)) .unwrap(); diff --git a/crates/core/tests/ml.rs b/crates/core/tests/ml_component.rs similarity index 96% rename from crates/core/tests/ml.rs rename to crates/core/tests/ml_component.rs index 4302c13352..351469335d 100644 --- a/crates/core/tests/ml.rs +++ b/crates/core/tests/ml_component.rs @@ -10,14 +10,14 @@ pub mod ml { use test::test::tensor; use test::test::errors::ErrorCode; - use test::test::graph::{Error, ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; + use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; use test::test::inference::GraphExecutionContext; use wasmtime::component::Resource; #[derive(Clone)] - pub struct NNHostComponent; + pub struct MLHostComponent; - impl HostComponent for NNHostComponent { + impl HostComponent for MLHostComponent { type Data = MLHostImpl; fn add_to_linker( @@ -138,6 +138,7 @@ pub mod ml { _graph_encoding: GraphEncoding, _target: ExecutionTarget, ) -> Result, Resource>, anyhow::Error> { + println!("[graph::Host] load"); Err(anyhow!("Not implemented")) } fn load_by_name( From 5430f6739d7cd390249901e6a0de3d698ff833e5 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 5 Jun 2024 17:44:16 +0300 Subject: [PATCH 10/26] A lot of progress happened today --- Cargo.lock | 1 + crates/core/Cargo.toml | 3 +- crates/core/tests/core-wasi-test/Cargo.toml | 1 + crates/core/tests/core-wasi-test/src/main.rs | 18 ++- crates/core/tests/integration_test.rs | 5 +- crates/core/tests/ml_component.rs | 129 +++++++++++++++++-- 6 files changed, 140 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2735eaeecc..685a803a8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7529,6 +7529,7 @@ dependencies = [ "spin-componentize", "spin-telemetry", "system-interface", + "table", "tempfile", "tokio", "tracing", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index c6e18e8a5c..dcaf6b75b0 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -20,7 +20,8 @@ tokio = "1.0" bytes = "1.0" spin-telemetry = { path = "../telemetry" } http = "1.0" -openvino = { version = "0.6.0", features = ["runtime-linking",], optional = true } +openvino = { version = "0.6.0", features = ["runtime-linking",] } +table = { path = "../table" } [target.'cfg(unix)'.dependencies] rustix = "0.37.19" diff --git a/crates/core/tests/core-wasi-test/Cargo.toml b/crates/core/tests/core-wasi-test/Cargo.toml index 1df9c0eb6f..e3fde5c310 100644 --- a/crates/core/tests/core-wasi-test/Cargo.toml +++ b/crates/core/tests/core-wasi-test/Cargo.toml @@ -8,5 +8,6 @@ debug = true [dependencies] wit-bindgen = "0.13.0" +image2tensor = "0.3.1" [workspace] diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 2dca577810..c208ea8069 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -29,6 +29,9 @@ use std::path::Path; use crate::hello::test::test::gggg2::say_hello; +use image2tensor; +use image2tensor::convert_image_to_tensor_bytes; + type Result = std::result::Result<(), Box>; fn main() -> Result { @@ -86,13 +89,20 @@ fn main() -> Result { println!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); let context = ml::test::test::graph::Graph::init_execution_context(&imagenet_graph).unwrap(); println!("Created context with ID: {:?}", context); - - let image_data = std::fs::read("images/0.jpg").unwrap(); + let tensor_dimensions:Vec = vec![1, 3, 224, 224]; + let tensor_data = convert_image_to_tensor_bytes( + "images/0.jpg", + tensor_dimensions[2], + tensor_dimensions[3], + image2tensor::TensorType::F32, + image2tensor::ColorOrder::BGR, + ) + .or_else(|e| Err(e)) + .unwrap(); - let tensor_data = image_data; // !! TODO !!convert image to tensor - let tensor_dimensions:Vec = vec![1, 3, 224, 224]; + let tensor_type = ml::test::test::tensor::TensorType::Fp32; let tensor_id = ml::test::test::tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); println!("Created tensor with ID: {:?}", tensor_id); diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 8322e85847..4784b3959a 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -17,6 +17,7 @@ use spin_core::{ use tempfile::TempDir; use tokio::{fs, io::AsyncWrite}; + #[tokio::test(flavor = "multi_thread")] async fn test_stdio() { let stdout = run_core_wasi_test(["echo"], |store_builder| { @@ -196,13 +197,13 @@ async fn test_host_component_imagenet() { let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../target/test-programs/imagenet"); println!("imagenet_path = {imagenet_path:?}"); - + let stdout = run_core_wasi_test_engine( &engine, ["imagenet", "/"], |store_builder| { store_builder.read_only_preopened_dir(&imagenet_path, "/".into()).unwrap(); - store_builder.host_components_data().set(handle, MLHostImpl {}); + //probably no needed! store_builder.host_components_data().set(handle, MLHostImpl {..Default::default()}); }, |_| {}, ) diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 351469335d..00068f524c 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -1,3 +1,4 @@ + pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); @@ -13,6 +14,10 @@ pub mod ml { use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; use test::test::inference::GraphExecutionContext; use wasmtime::component::Resource; + use tokio::sync::Mutex; + + use table; + use openvino::{Core, InferenceError, Layout, Precision, SetupError, TensorDesc}; #[derive(Clone)] pub struct MLHostComponent; @@ -28,21 +33,86 @@ pub mod ml { } fn build_data(&self) -> Self::Data { - MLHostImpl {} + MLHostImpl { ..Default::default()} } } - pub struct MLHostImpl {} + pub struct GraphInternalData { + pub xml: Vec, + pub weights: Vec, + pub target: ExecutionTarget, + } + + + pub struct GraphExecutionContextInternalData { + cnn_network: openvino::CNNNetwork, + executable_network: Mutex, + } + + #[derive(Default)] + pub struct MLHostImpl { + pub openvino: Option, + pub graphs: table::Table, + pub execution_contexts: table::Table, + } impl graph::HostGraph for MLHostImpl { fn init_execution_context( &mut self, - _graph: Resource, + graph: Resource, ) -> Result< Result, Resource>, anyhow::Error, > { - Err(anyhow!("Not implemented")) + // Construct the context if none is present; this is done lazily (i.e. + // upon actually loading a model) because it may fail to find and load + // the OpenVINO libraries. The laziness limits the extent of the error + // only to wasi-nn users, not all WASI users. + if self.openvino.is_none() { + self.openvino.replace(openvino::Core::new(None)?); + } + if self.openvino.is_some() { + if let Some(graph) = self.graphs.get(graph.rep()) { + let mut cnn_network = self.openvino.as_mut().expect("").read_network_from_buffer(&graph.xml, &graph.weights)?; + + // Construct OpenVINO graph structures: `cnn_network` contains the graph + // structure, `exec_network` can perform inference. + //let core = self + // .0 + // .as_mut() + // .expect("openvino::Core was previously constructed"); + //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; + + // TODO: this is a temporary workaround. We need a more elegant way to + // specify the layout in the long run. However, without this newer + // versions of OpenVINO will fail due to parameter mismatch. + for i in 0..cnn_network.get_inputs_len().unwrap() { + let name = cnn_network.get_input_name(i)?; + cnn_network.set_input_layout(&name, Layout::NHWC)?; + }; + + let exec_network = self.openvino.as_mut().expect("").load_network(&cnn_network, map_execution_target_to_string(graph.target))?; + + let graph_execution_context = GraphExecutionContextInternalData { + cnn_network: cnn_network, + executable_network: Mutex::new(exec_network) + }; + + if let Ok(graph_execution_context_id) = self.execution_contexts.push(graph_execution_context).map(Resource::::new_own){ + println!("{:?}", graph_execution_context_id); + return Ok(Ok(graph_execution_context_id)) + }; + + //.ok_or(sqlite::Error::InvalidConnection); + /*let box_: Box = Box::new(OpenvinoGraph( + Arc::new(cnn_network), + Arc::new(Mutex::new(exec_network)), + )); + Ok(box_.into()) + */ + } + } + Err(anyhow!("[graph::HostGraph] fn init_execution_context -> Not implemented")) } fn drop(&mut self, _graph: Resource) -> Result<(), anyhow::Error> { Err(anyhow!("Not implemented")) @@ -134,21 +204,60 @@ pub mod ml { impl graph::Host for MLHostImpl { fn load( &mut self, - _graph: Vec, - _graph_encoding: GraphEncoding, - _target: ExecutionTarget, + graph: Vec, + graph_encoding: GraphEncoding, + target: ExecutionTarget, ) -> Result, Resource>, anyhow::Error> { - println!("[graph::Host] load"); - Err(anyhow!("Not implemented")) + println!("[graph::Host] load entry point"); + if graph.len() != 2 { + return Err(anyhow!("Expected 2 elements in graph builder vector")) + } + if graph_encoding != GraphEncoding::Openvino { + return Err(anyhow!("Only OpenVINO encoding is supported")) + } + // Read the guest array. + let graph_internal_data = GraphInternalData { xml: graph[0].clone(), weights: graph[1].clone(), target: target }; + if let Ok(graph_id) = self.graphs.push(graph_internal_data).map(Resource::::new_own){ + println!("{:?}", graph_id); + return Ok(Ok(graph_id)) + };//.ok_or(sqlite::Error::InvalidConnection); + + Err(anyhow!("[graph::Host] fn load -> Not implemented")) } fn load_by_name( &mut self, _graph: String, ) -> Result, Resource>, anyhow::Error> { - Err(anyhow!("Not implemented")) + Err(anyhow!("[graph::Host] fn load_by_name -> Not implemented")) } } impl inference::Host for MLHostImpl {} impl tensor::Host for MLHostImpl {} + + + + /// Return the execution target string expected by OpenVINO from the + /// `ExecutionTarget` enum provided by wasi-nn. + fn map_execution_target_to_string(target: ExecutionTarget) -> &'static str { + match target { + ExecutionTarget::Cpu => "CPU", + ExecutionTarget::Gpu => "GPU", + ExecutionTarget::Tpu => unimplemented!("OpenVINO does not support TPU execution targets"), + } + } + + /// Return OpenVINO's precision type for the `TensorType` enum provided by + /// wasi-nn. + fn map_tensor_type_to_precision(tensor_type: tensor::TensorType) -> openvino::Precision { + match tensor_type { + tensor::TensorType::Fp16 => Precision::FP16, + tensor::TensorType::Fp32 => Precision::FP32, + tensor::TensorType::Fp64 => Precision::FP64, + tensor::TensorType::U8 => Precision::U8, + tensor::TensorType::I32 => Precision::I32, + tensor::TensorType::I64 => Precision::I64, + tensor::TensorType::Bf16 => todo!("not yet supported in `openvino` bindings"), + } + } } From 7755910094b7447956be996e4b2eca11718e62a1 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 10 Jun 2024 17:48:08 +0300 Subject: [PATCH 11/26] Masters of driving --- crates/core/tests/core-wasi-test/src/main.rs | 4 +- crates/core/tests/integration_test.rs | 2 +- crates/core/tests/ml_component.rs | 299 ++++++++++++++----- 3 files changed, 231 insertions(+), 74 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index c208ea8069..0cdd52776e 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -107,12 +107,12 @@ fn main() -> Result { let tensor_id = ml::test::test::tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); println!("Created tensor with ID: {:?}", tensor_id); - let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0.jpg", tensor_id).unwrap(); + let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); println!("Input set with ID: {:?}", set_input_result); let infered_result = ml::test::test::inference::GraphExecutionContext::compute(&context).unwrap(); println!("Executed graph inference"); - let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0.jpg").unwrap(); + let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0").unwrap(); let output_result = ml::test::test::tensor::Tensor::data(&output_result_id); println!("output = {:?}", &output_result); diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 4784b3959a..e1aebabf18 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -191,7 +191,7 @@ async fn test_host_component_nn() { #[tokio::test(flavor = "multi_thread")] async fn test_host_component_imagenet() { let engine = test_engine(); - let handle = engine + let _handle = engine .find_host_component_handle::() .unwrap(); let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 00068f524c..cdcf03456d 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -2,9 +2,10 @@ pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); + use anyhow::Ok; use spin_core::HostComponent; - use anyhow::anyhow; + use anyhow::{anyhow, Context}; use test::test::errors; use test::test::graph; use test::test::inference; @@ -17,7 +18,7 @@ pub mod ml { use tokio::sync::Mutex; use table; - use openvino::{Core, InferenceError, Layout, Precision, SetupError, TensorDesc}; + use openvino::{Core, InferenceError, Layout, Precision, SetupError, TensorDesc, InferRequest}; #[derive(Clone)] pub struct MLHostComponent; @@ -36,7 +37,7 @@ pub mod ml { MLHostImpl { ..Default::default()} } } - + #[derive(Debug)] pub struct GraphInternalData { pub xml: Vec, pub weights: Vec, @@ -45,8 +46,20 @@ pub mod ml { pub struct GraphExecutionContextInternalData { - cnn_network: openvino::CNNNetwork, - executable_network: Mutex, + pub cnn_network: openvino::CNNNetwork, + pub executable_network: Mutex, + pub infer_request: openvino::InferRequest, + } + + pub struct TensorInternalData { + tensor_dimensions: tensor::TensorDimensions, + tensor_type: tensor::TensorType, + tensor_data: tensor::TensorData, + } + + pub struct ErrorInternalData { + code: errors::ErrorCode, + message: String, } #[derive(Default)] @@ -54,6 +67,9 @@ pub mod ml { pub openvino: Option, pub graphs: table::Table, pub execution_contexts: table::Table, + pub tensors: table::Table, + pub errors: table::Table, + } impl graph::HostGraph for MLHostImpl { @@ -75,128 +91,241 @@ pub mod ml { if let Some(graph) = self.graphs.get(graph.rep()) { let mut cnn_network = self.openvino.as_mut().expect("").read_network_from_buffer(&graph.xml, &graph.weights)?; - // Construct OpenVINO graph structures: `cnn_network` contains the graph - // structure, `exec_network` can perform inference. - //let core = self - // .0 - // .as_mut() - // .expect("openvino::Core was previously constructed"); - //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; + // Construct OpenVINO graph structures: `cnn_network` contains the graph + // structure, `exec_network` can perform inference. + //let core = self + // .0 + // .as_mut() + // .expect("openvino::Core was previously constructed"); + //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; - // TODO: this is a temporary workaround. We need a more elegant way to - // specify the layout in the long run. However, without this newer - // versions of OpenVINO will fail due to parameter mismatch. - for i in 0..cnn_network.get_inputs_len().unwrap() { - let name = cnn_network.get_input_name(i)?; - cnn_network.set_input_layout(&name, Layout::NHWC)?; - }; + // TODO: this is a temporary workaround. We need a more elegant way to + // specify the layout in the long run. However, without this newer + // versions of OpenVINO will fail due to parameter mismatch. + for i in 0..cnn_network.get_inputs_len().unwrap() { + let name = cnn_network.get_input_name(i)?; + cnn_network.set_input_layout(&name, Layout::NHWC)?; + }; - let exec_network = self.openvino.as_mut().expect("").load_network(&cnn_network, map_execution_target_to_string(graph.target))?; - - let graph_execution_context = GraphExecutionContextInternalData { + let mut exec_network = self.openvino.as_mut().expect("").load_network(&cnn_network, map_execution_target_to_string(graph.target))?; + let infer_request = exec_network.create_infer_request().unwrap(); + let graph_execution_context = GraphExecutionContextInternalData { cnn_network: cnn_network, - executable_network: Mutex::new(exec_network) - }; + executable_network: Mutex::new(exec_network), + infer_request: infer_request, + }; + + match self.execution_contexts.push(graph_execution_context).map(Resource::::new_own) { + std::result::Result::Ok(res) => { + return std::result::Result::Ok(std::result::Result::Ok(res)); + } + Err(_) => { + match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: "Can't create graph execution context".to_string()}){ + std::result::Result::Ok(id) => { + return std::result::Result::Ok(Err(Resource::::new_own(id))); + } + Err(_) => { + return Err(anyhow!("Can't allocate error")); + } + } + //.map(Resource::::new_own) + // .map_err(|_|anyhow!("Can't allocate error"))); - if let Ok(graph_execution_context_id) = self.execution_contexts.push(graph_execution_context).map(Resource::::new_own){ - println!("{:?}", graph_execution_context_id); - return Ok(Ok(graph_execution_context_id)) - }; + //return std::result::Result::Ok(Err(err)); + } + } + /* + if let std::result::Result::Ok(graph_execution_context_id) = + self.execution_contexts.push(graph_execution_context) + .map(Resource::::new_own) + + { + println!("graph_execution_context_id = {:?}", graph_execution_context_id); + return std::result::Result::Ok(std::result::Result::Ok(graph_execution_context_id)) + }; + */ - //.ok_or(sqlite::Error::InvalidConnection); - /*let box_: Box = Box::new(OpenvinoGraph( - Arc::new(cnn_network), - Arc::new(Mutex::new(exec_network)), - )); - Ok(box_.into()) - */ } } Err(anyhow!("[graph::HostGraph] fn init_execution_context -> Not implemented")) } - fn drop(&mut self, _graph: Resource) -> Result<(), anyhow::Error> { - Err(anyhow!("Not implemented")) + fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { + let v = self.graphs.remove(graph.rep()); + //println!("v = {v:?}"); + v.ok_or(anyhow!(format!("Can't find graph with ID = {}", graph.rep()))).map(|_| ()) + } } impl errors::HostError for MLHostImpl { fn new( &mut self, - _code: errors::ErrorCode, - _data: String, + code: errors::ErrorCode, + data: String, ) -> Result, anyhow::Error> { - Err(anyhow!("Not implemented")) + self.errors.push(ErrorInternalData { code: code, message:data}).map(Resource::::new_own).map_err(|_|anyhow!("Can't allocate error")) } - fn drop(&mut self, _error: Resource) -> Result<(), anyhow::Error> { - Err(anyhow!("Not implemented")) + fn drop(&mut self, error: Resource) -> Result<(), anyhow::Error> { + self.errors.remove(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|_| ()) } - fn code(&mut self, _error: Resource) -> Result { - Err(anyhow!("Not implemented")) + fn code(&mut self, error: Resource) -> Result { + self.errors.get(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|e| e.code) } - fn data(&mut self, _error: Resource) -> Result { - Err(anyhow!("Not implemented")) + fn data(&mut self, error: Resource) -> Result { + self.errors.get(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|e| e.message.clone()) + } } impl tensor::HostTensor for MLHostImpl { fn new( &mut self, - _tensor_dimentions: tensor::TensorDimensions, - _tensor_type: tensor::TensorType, - _tensor_data: tensor::TensorData, + tensor_dimensions: tensor::TensorDimensions, + tensor_type: tensor::TensorType, + tensor_data: tensor::TensorData, ) -> Result, anyhow::Error> { - Err(anyhow!("Not implemented")) + let tensor = TensorInternalData { + tensor_dimensions: tensor_dimensions, + tensor_type: tensor_type, + tensor_data: tensor_data, + }; + self.tensors.push(tensor).map(Resource::::new_own).map_err(|_|anyhow!("Can't allocate tensor")) } fn dimensions( &mut self, - _tensor: Resource, + tensor: Resource, ) -> Result, anyhow::Error> { - Err(anyhow!("Not implemented")) + self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_dimensions.clone()) } fn ty( &mut self, - _tensor: Resource, + tensor: Resource, ) -> Result { - Err(anyhow!("Not implemented")) + self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_type) } - fn data(&mut self, _tensor: Resource) -> Result { - Err(anyhow!("Not implemented")) + fn data(&mut self, tensor: Resource) -> Result { + self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_data.clone()) } - fn drop(&mut self, _tensor: Resource) -> Result<(), anyhow::Error> { - Err(anyhow!("Not implemented")) + fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { + self.tensors.remove(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|_| ()) } } impl inference::HostGraphExecutionContext for MLHostImpl { fn set_input( &mut self, - _graph_execution_context: Resource, - _input_name: String, - _tensor: Resource, + graph_execution_context: Resource, + input_name: String, + tensor: Resource, ) -> Result>, anyhow::Error> { - Err(anyhow!("Not implemented")) + let index = input_name.parse().context("Can't parse {} to usize for input_name")?; + // Construct the blob structure. TODO: there must be some good way to + // discover the layout here; `desc` should not have to default to NHWC. + let tensor_resource = self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep())))?; + let precision = map_tensor_type_to_precision(tensor_resource.tensor_type); + let dimensions = tensor_resource.tensor_dimensions + .iter() + .map(|&d| d as usize) + .collect::>(); + let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); + let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data)?; + let execution_context: &mut GraphExecutionContextInternalData = self.execution_contexts.get_mut(graph_execution_context.rep()).context(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep()))?; + + let input_name = execution_context.cnn_network.get_input_name(index).context(format!("Can't find input with name = {}", index))?; + match execution_context.infer_request.set_blob(&input_name, &blob) { + std::result::Result::Ok(res) => { + return std::result::Result::Ok(std::result::Result::Ok(res)); + } + std::result::Result::Err(err) => { + match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { + std::result::Result::Ok(error_rep) => { + return Ok(Err(Resource::::new_own(error_rep))); + } + Err(err) => { + return Err(anyhow!("Can't create internal error for {:?}", err)); + } + } + } + } } + fn compute( &mut self, - _graph_execution_context: Resource, + graph_execution_context: Resource, ) -> Result>, anyhow::Error> { - Err(anyhow!("Not implemented")) + let graph_execution = self.execution_contexts + .get_mut(graph_execution_context.rep()) + .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; + match graph_execution.infer_request.infer() { + std::result::Result::Ok(..) => { return std::result::Result::Ok(std::result::Result::Ok(())); } + Err(err) => { + match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { + std::result::Result::Ok(error_rep) => { + return Ok(Err(Resource::::new_own(error_rep))); + } + Err(err) => { + return Err(anyhow!("Can't create internal error for {:?}", err)); + } + } + } + } } + fn get_output( &mut self, - _graph_execution_context: Resource, - _input_name: String, + graph_execution_context: Resource, + input_name: String, ) -> Result, Resource>, anyhow::Error> { - Err(anyhow!("Not implemented")) + let index = input_name.parse::().context("Can't parse {} to usize for input_name")?; + let graph_execution = self.execution_contexts + .get_mut(graph_execution_context.rep()) + .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; + let output_name = graph_execution.cnn_network.get_output_name(index).context("Can't find output name for ID = {index}")?; + let blob = graph_execution.infer_request.get_blob(&output_name).context("Can't get blob for output name = {output_name}")?; + //let blob_size = blob.byte_len()?; + let tensor_desc = blob.tensor_desc().context("Can't get blob description")?; + let buffer = blob.buffer() + //let buffer = blob.buffer_as_type::() + .context("Can't get blob buffer")?.iter() + .map(|&d| d as u8) + .collect::>(); + let tensor_dimensions = tensor_desc.dims().iter() + .map(|&d| d as u32) + .collect::>(); + + let tensor = TensorInternalData { + tensor_dimensions:tensor_dimensions, + tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), + tensor_data: buffer, + }; + match self.tensors.push(tensor).map(Resource::::new_own) { + std::result::Result::Ok(t) => { + return std::result::Result::Ok(std::result::Result::Ok(t)); + } + Err(err) => { + match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { + std::result::Result::Ok(error_rep) => { + return Ok(Err(Resource::::new_own(error_rep))); + } + Err(err) => { + return Err(anyhow!("Can't create internal error for {:?}", err)); + } + } + } + } } + fn drop( &mut self, - _graph_execution_context: Resource, + graph_execution_context: Resource, ) -> Result<(), anyhow::Error> { - Err(anyhow!("Not implemented")) + let id = graph_execution_context.rep(); + self.graphs.remove(id).context("{Can't drow GraphExecutionContext with id = {id}")?; + Ok(()) + } } @@ -217,12 +346,29 @@ pub mod ml { } // Read the guest array. let graph_internal_data = GraphInternalData { xml: graph[0].clone(), weights: graph[1].clone(), target: target }; - if let Ok(graph_id) = self.graphs.push(graph_internal_data).map(Resource::::new_own){ + match self.graphs.push(graph_internal_data) { + std::result::Result::Ok(graph_rep) => { + return std::result::Result::Ok(std::result::Result::Ok(Resource::::new_own(graph_rep))); + }, + Err(err) => { + match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { + std::result::Result::Ok(error_rep) => { + return Ok(Err(Resource::::new_own(error_rep))); + } + Err(err) => { + return Err(anyhow!("Can't create internal error for {:?}", err)); + } + } + } + } + /*/ + if let std::result::Result::Ok(graph_id) = self.graphs.push(graph_internal_data).map(Resource::::new_own){ println!("{:?}", graph_id); - return Ok(Ok(graph_id)) + return std::result::Result::Ok(std::result::Result::Ok(graph_id)) };//.ok_or(sqlite::Error::InvalidConnection); Err(anyhow!("[graph::Host] fn load -> Not implemented")) + */ } fn load_by_name( &mut self, @@ -260,4 +406,15 @@ pub mod ml { tensor::TensorType::Bf16 => todo!("not yet supported in `openvino` bindings"), } } + fn map_precision_to_tensor_type(precision: openvino::Precision) -> tensor::TensorType { + match precision { + Precision::FP16 => tensor::TensorType::Fp16, + Precision::FP32 => tensor::TensorType::Fp32, + Precision::FP64 => tensor::TensorType::Fp64, + Precision::U8 => tensor::TensorType::U8, + Precision::I32 => tensor::TensorType::I32, + Precision::I64 => tensor::TensorType::I64, + _ => todo!("not yet supported in `openvino` bindings"), + } + } } From b119962de2f3e9f4bff3977d70aa07b730ae7524 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 11 Jun 2024 14:56:31 +0300 Subject: [PATCH 12/26] Host component works for both CPU and GPU inference of NN --- .../core-wasi-test/src/imagenet_classes.rs | 1043 +++++++++++++++++ crates/core/tests/core-wasi-test/src/main.rs | 42 +- crates/core/tests/integration_test.rs | 5 +- crates/core/tests/ml_component.rs | 66 +- 4 files changed, 1094 insertions(+), 62 deletions(-) create mode 100644 crates/core/tests/core-wasi-test/src/imagenet_classes.rs diff --git a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs new file mode 100644 index 0000000000..6081390e51 --- /dev/null +++ b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs @@ -0,0 +1,1043 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +/* The code in this file is adapted from https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/imagenet_classes.ts */ + +pub const IMAGENET_CLASSES: [&str; 1000] = [ + "tench, Tinca tinca", + "goldfish, Carassius auratus", + "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias", + "tiger shark, Galeocerdo cuvieri", + "hammerhead, hammerhead shark", + "electric ray, crampfish, numbfish, torpedo", + "stingray", + "cock", + "hen", + "ostrich, Struthio camelus", + "brambling, Fringilla montifringilla", + "goldfinch, Carduelis carduelis", + "house finch, linnet, Carpodacus mexicanus", + "junco, snowbird", + "indigo bunting, indigo finch, indigo bird, Passerina cyanea", + "robin, American robin, Turdus migratorius", + "bulbul", + "jay", + "magpie", + "chickadee", + "water ouzel, dipper", + "kite", + "bald eagle, American eagle, Haliaeetus leucocephalus", + "vulture", + "great grey owl, great gray owl, Strix nebulosa", + "European fire salamander, Salamandra salamandra", + "common newt, Triturus vulgaris", + "eft", + "spotted salamander, Ambystoma maculatum", + "axolotl, mud puppy, Ambystoma mexicanum", + "bullfrog, Rana catesbeiana", + "tree frog, tree-frog", + "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", + "loggerhead, loggerhead turtle, Caretta caretta", + "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea", + "mud turtle", + "terrapin", + "box turtle, box tortoise", + "banded gecko", + "common iguana, iguana, Iguana iguana", + "American chameleon, anole, Anolis carolinensis", + "whiptail, whiptail lizard", + "agama", + "frilled lizard, Chlamydosaurus kingi", + "alligator lizard", + "Gila monster, Heloderma suspectum", + "green lizard, Lacerta viridis", + "African chameleon, Chamaeleo chamaeleon", + "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis", + "African crocodile, Nile crocodile, Crocodylus niloticus", + "American alligator, Alligator mississipiensis", + "triceratops", + "thunder snake, worm snake, Carphophis amoenus", + "ringneck snake, ring-necked snake, ring snake", + "hognose snake, puff adder, sand viper", + "green snake, grass snake", + "king snake, kingsnake", + "garter snake, grass snake", + "water snake", + "vine snake", + "night snake, Hypsiglena torquata", + "boa constrictor, Constrictor constrictor", + "rock python, rock snake, Python sebae", + "Indian cobra, Naja naja", + "green mamba", + "sea snake", + "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus", + "diamondback, diamondback rattlesnake, Crotalus adamanteus", + "sidewinder, horned rattlesnake, Crotalus cerastes", + "trilobite", + "harvestman, daddy longlegs, Phalangium opilio", + "scorpion", + "black and gold garden spider, Argiope aurantia", + "barn spider, Araneus cavaticus", + "garden spider, Aranea diademata", + "black widow, Latrodectus mactans", + "tarantula", + "wolf spider, hunting spider", + "tick", + "centipede", + "black grouse", + "ptarmigan", + "ruffed grouse, partridge, Bonasa umbellus", + "prairie chicken, prairie grouse, prairie fowl", + "peacock", + "quail", + "partridge", + "African grey, African gray, Psittacus erithacus", + "macaw", + "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita", + "lorikeet", + "coucal", + "bee eater", + "hornbill", + "hummingbird", + "jacamar", + "toucan", + "drake", + "red-breasted merganser, Mergus serrator", + "goose", + "black swan, Cygnus atratus", + "tusker", + "echidna, spiny anteater, anteater", + "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus", + "wallaby, brush kangaroo", + "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus", + "wombat", + "jelly fish", + "sea anemone, anemone", + "brain coral", + "flatworm, platyhelminth", + "nematode, nematode worm, roundworm", + "conch", + "snail", + "slug", + "sea slug, nudibranch", + "chiton, coat-of-mail shell, sea cradle, polyplacophore", + "chambered nautilus, pearly nautilus, nautilus", + "Dungeness crab, Cancer magister", + "rock crab, Cancer irroratus", + "fiddler crab", + "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica", + "American lobster, Northern lobster, Maine lobster, Homarus americanus", + "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish", + "crayfish, crawfish, crawdad, crawdaddy", + "hermit crab", + "isopod", + "white stork, Ciconia ciconia", + "black stork, Ciconia nigra", + "spoonbill", + "flamingo", + "little blue heron, Egretta caerulea", + "American egret, great white heron, Egretta albus", + "bittern", + "crane", + "limpkin, Aramus pictus", + "European gallinule, Porphyrio porphyrio", + "American coot, marsh hen, mud hen, water hen, Fulica americana", + "bustard", + "ruddy turnstone, Arenaria interpres", + "red-backed sandpiper, dunlin, Erolia alpina", + "redshank, Tringa totanus", + "dowitcher", + "oystercatcher, oyster catcher", + "pelican", + "king penguin, Aptenodytes patagonica", + "albatross, mollymawk", + "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus", + "killer whale, killer, orca, grampus, sea wolf, Orcinus orca", + "dugong, Dugong dugon", + "sea lion", + "Chihuahua", + "Japanese spaniel", + "Maltese dog, Maltese terrier, Maltese", + "Pekinese, Pekingese, Peke", + "Shih-Tzu", + "Blenheim spaniel", + "papillon", + "toy terrier", + "Rhodesian ridgeback", + "Afghan hound, Afghan", + "basset, basset hound", + "beagle", + "bloodhound, sleuthhound", + "bluetick", + "black-and-tan coonhound", + "Walker hound, Walker foxhound", + "English foxhound", + "redbone", + "borzoi, Russian wolfhound", + "Irish wolfhound", + "Italian greyhound", + "whippet", + "Ibizan hound, Ibizan Podenco", + "Norwegian elkhound, elkhound", + "otterhound, otter hound", + "Saluki, gazelle hound", + "Scottish deerhound, deerhound", + "Weimaraner", + "Staffordshire bullterrier, Staffordshire bull terrier", + "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier", + "Bedlington terrier", + "Border terrier", + "Kerry blue terrier", + "Irish terrier", + "Norfolk terrier", + "Norwich terrier", + "Yorkshire terrier", + "wire-haired fox terrier", + "Lakeland terrier", + "Sealyham terrier, Sealyham", + "Airedale, Airedale terrier", + "cairn, cairn terrier", + "Australian terrier", + "Dandie Dinmont, Dandie Dinmont terrier", + "Boston bull, Boston terrier", + "miniature schnauzer", + "giant schnauzer", + "standard schnauzer", + "Scotch terrier, Scottish terrier, Scottie", + "Tibetan terrier, chrysanthemum dog", + "silky terrier, Sydney silky", + "soft-coated wheaten terrier", + "West Highland white terrier", + "Lhasa, Lhasa apso", + "flat-coated retriever", + "curly-coated retriever", + "golden retriever", + "Labrador retriever", + "Chesapeake Bay retriever", + "German short-haired pointer", + "vizsla, Hungarian pointer", + "English setter", + "Irish setter, red setter", + "Gordon setter", + "Brittany spaniel", + "clumber, clumber spaniel", + "English springer, English springer spaniel", + "Welsh springer spaniel", + "cocker spaniel, English cocker spaniel, cocker", + "Sussex spaniel", + "Irish water spaniel", + "kuvasz", + "schipperke", + "groenendael", + "malinois", + "briard", + "kelpie", + "komondor", + "Old English sheepdog, bobtail", + "Shetland sheepdog, Shetland sheep dog, Shetland", + "collie", + "Border collie", + "Bouvier des Flandres, Bouviers des Flandres", + "Rottweiler", + "German shepherd, German shepherd dog, German police dog, alsatian", + "Doberman, Doberman pinscher", + "miniature pinscher", + "Greater Swiss Mountain dog", + "Bernese mountain dog", + "Appenzeller", + "EntleBucher", + "boxer", + "bull mastiff", + "Tibetan mastiff", + "French bulldog", + "Great Dane", + "Saint Bernard, St Bernard", + "Eskimo dog, husky", + "malamute, malemute, Alaskan malamute", + "Siberian husky", + "dalmatian, coach dog, carriage dog", + "affenpinscher, monkey pinscher, monkey dog", + "basenji", + "pug, pug-dog", + "Leonberg", + "Newfoundland, Newfoundland dog", + "Great Pyrenees", + "Samoyed, Samoyede", + "Pomeranian", + "chow, chow chow", + "keeshond", + "Brabancon griffon", + "Pembroke, Pembroke Welsh corgi", + "Cardigan, Cardigan Welsh corgi", + "toy poodle", + "miniature poodle", + "standard poodle", + "Mexican hairless", + "timber wolf, grey wolf, gray wolf, Canis lupus", + "white wolf, Arctic wolf, Canis lupus tundrarum", + "red wolf, maned wolf, Canis rufus, Canis niger", + "coyote, prairie wolf, brush wolf, Canis latrans", + "dingo, warrigal, warragal, Canis dingo", + "dhole, Cuon alpinus", + "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus", + "hyena, hyaena", + "red fox, Vulpes vulpes", + "kit fox, Vulpes macrotis", + "Arctic fox, white fox, Alopex lagopus", + "grey fox, gray fox, Urocyon cinereoargenteus", + "tabby, tabby cat", + "tiger cat", + "Persian cat", + "Siamese cat, Siamese", + "Egyptian cat", + "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor", + "lynx, catamount", + "leopard, Panthera pardus", + "snow leopard, ounce, Panthera uncia", + "jaguar, panther, Panthera onca, Felis onca", + "lion, king of beasts, Panthera leo", + "tiger, Panthera tigris", + "cheetah, chetah, Acinonyx jubatus", + "brown bear, bruin, Ursus arctos", + "American black bear, black bear, Ursus americanus, Euarctos americanus", + "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus", + "sloth bear, Melursus ursinus, Ursus ursinus", + "mongoose", + "meerkat, mierkat", + "tiger beetle", + "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle", + "ground beetle, carabid beetle", + "long-horned beetle, longicorn, longicorn beetle", + "leaf beetle, chrysomelid", + "dung beetle", + "rhinoceros beetle", + "weevil", + "fly", + "bee", + "ant, emmet, pismire", + "grasshopper, hopper", + "cricket", + "walking stick, walkingstick, stick insect", + "cockroach, roach", + "mantis, mantid", + "cicada, cicala", + "leafhopper", + "lacewing, lacewing fly", + "dragonfly, darning needle, devil\"s darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", + "damselfly", + "admiral", + "ringlet, ringlet butterfly", + "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus", + "cabbage butterfly", + "sulphur butterfly, sulfur butterfly", + "lycaenid, lycaenid butterfly", + "starfish, sea star", + "sea urchin", + "sea cucumber, holothurian", + "wood rabbit, cottontail, cottontail rabbit", + "hare", + "Angora, Angora rabbit", + "hamster", + "porcupine, hedgehog", + "fox squirrel, eastern fox squirrel, Sciurus niger", + "marmot", + "beaver", + "guinea pig, Cavia cobaya", + "sorrel", + "zebra", + "hog, pig, grunter, squealer, Sus scrofa", + "wild boar, boar, Sus scrofa", + "warthog", + "hippopotamus, hippo, river horse, Hippopotamus amphibius", + "ox", + "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis", + "bison", + "ram, tup", + "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis", + "ibex, Capra ibex", + "hartebeest", + "impala, Aepyceros melampus", + "gazelle", + "Arabian camel, dromedary, Camelus dromedarius", + "llama", + "weasel", + "mink", + "polecat, fitch, foulmart, foumart, Mustela putorius", + "black-footed ferret, ferret, Mustela nigripes", + "otter", + "skunk, polecat, wood pussy", + "badger", + "armadillo", + "three-toed sloth, ai, Bradypus tridactylus", + "orangutan, orang, orangutang, Pongo pygmaeus", + "gorilla, Gorilla gorilla", + "chimpanzee, chimp, Pan troglodytes", + "gibbon, Hylobates lar", + "siamang, Hylobates syndactylus, Symphalangus syndactylus", + "guenon, guenon monkey", + "patas, hussar monkey, Erythrocebus patas", + "baboon", + "macaque", + "langur", + "colobus, colobus monkey", + "proboscis monkey, Nasalis larvatus", + "marmoset", + "capuchin, ringtail, Cebus capucinus", + "howler monkey, howler", + "titi, titi monkey", + "spider monkey, Ateles geoffroyi", + "squirrel monkey, Saimiri sciureus", + "Madagascar cat, ring-tailed lemur, Lemur catta", + "indri, indris, Indri indri, Indri brevicaudatus", + "Indian elephant, Elephas maximus", + "African elephant, Loxodonta africana", + "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens", + "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", + "barracouta, snoek", + "eel", + "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch", + "rock beauty, Holocanthus tricolor", + "anemone fish", + "sturgeon", + "gar, garfish, garpike, billfish, Lepisosteus osseus", + "lionfish", + "puffer, pufferfish, blowfish, globefish", + "abacus", + "abaya", + "academic gown, academic robe, judge\"s robe", + "accordion, piano accordion, squeeze box", + "acoustic guitar", + "aircraft carrier, carrier, flattop, attack aircraft carrier", + "airliner", + "airship, dirigible", + "altar", + "ambulance", + "amphibian, amphibious vehicle", + "analog clock", + "apiary, bee house", + "apron", + "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin", + "assault rifle, assault gun", + "backpack, back pack, knapsack, packsack, rucksack, haversack", + "bakery, bakeshop, bakehouse", + "balance beam, beam", + "balloon", + "ballpoint, ballpoint pen, ballpen, Biro", + "Band Aid", + "banjo", + "bannister, banister, balustrade, balusters, handrail", + "barbell", + "barber chair", + "barbershop", + "barn", + "barometer", + "barrel, cask", + "barrow, garden cart, lawn cart, wheelbarrow", + "baseball", + "basketball", + "bassinet", + "bassoon", + "bathing cap, swimming cap", + "bath towel", + "bathtub, bathing tub, bath, tub", + "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", + "beacon, lighthouse, beacon light, pharos", + "beaker", + "bearskin, busby, shako", + "beer bottle", + "beer glass", + "bell cote, bell cot", + "bib", + "bicycle-built-for-two, tandem bicycle, tandem", + "bikini, two-piece", + "binder, ring-binder", + "binoculars, field glasses, opera glasses", + "birdhouse", + "boathouse", + "bobsled, bobsleigh, bob", + "bolo tie, bolo, bola tie, bola", + "bonnet, poke bonnet", + "bookcase", + "bookshop, bookstore, bookstall", + "bottlecap", + "bow", + "bow tie, bow-tie, bowtie", + "brass, memorial tablet, plaque", + "brassiere, bra, bandeau", + "breakwater, groin, groyne, mole, bulwark, seawall, jetty", + "breastplate, aegis, egis", + "broom", + "bucket, pail", + "buckle", + "bulletproof vest", + "bullet train, bullet", + "butcher shop, meat market", + "cab, hack, taxi, taxicab", + "caldron, cauldron", + "candle, taper, wax light", + "cannon", + "canoe", + "can opener, tin opener", + "cardigan", + "car mirror", + "carousel, carrousel, merry-go-round, roundabout, whirligig", + "carpenter\"s kit, tool kit", + "carton", + "car wheel", + "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM", + "cassette", + "cassette player", + "castle", + "catamaran", + "CD player", + "cello, violoncello", + "cellular telephone, cellular phone, cellphone, cell, mobile phone", + "chain", + "chainlink fence", + "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour", + "chain saw, chainsaw", + "chest", + "chiffonier, commode", + "chime, bell, gong", + "china cabinet, china closet", + "Christmas stocking", + "church, church building", + "cinema, movie theater, movie theatre, movie house, picture palace", + "cleaver, meat cleaver, chopper", + "cliff dwelling", + "cloak", + "clog, geta, patten, sabot", + "cocktail shaker", + "coffee mug", + "coffeepot", + "coil, spiral, volute, whorl, helix", + "combination lock", + "computer keyboard, keypad", + "confectionery, confectionary, candy store", + "container ship, containership, container vessel", + "convertible", + "corkscrew, bottle screw", + "cornet, horn, trumpet, trump", + "cowboy boot", + "cowboy hat, ten-gallon hat", + "cradle", + "crane", + "crash helmet", + "crate", + "crib, cot", + "Crock Pot", + "croquet ball", + "crutch", + "cuirass", + "dam, dike, dyke", + "desk", + "desktop computer", + "dial telephone, dial phone", + "diaper, nappy, napkin", + "digital clock", + "digital watch", + "dining table, board", + "dishrag, dishcloth", + "dishwasher, dish washer, dishwashing machine", + "disk brake, disc brake", + "dock, dockage, docking facility", + "dogsled, dog sled, dog sleigh", + "dome", + "doormat, welcome mat", + "drilling platform, offshore rig", + "drum, membranophone, tympan", + "drumstick", + "dumbbell", + "Dutch oven", + "electric fan, blower", + "electric guitar", + "electric locomotive", + "entertainment center", + "envelope", + "espresso maker", + "face powder", + "feather boa, boa", + "file, file cabinet, filing cabinet", + "fireboat", + "fire engine, fire truck", + "fire screen, fireguard", + "flagpole, flagstaff", + "flute, transverse flute", + "folding chair", + "football helmet", + "forklift", + "fountain", + "fountain pen", + "four-poster", + "freight car", + "French horn, horn", + "frying pan, frypan, skillet", + "fur coat", + "garbage truck, dustcart", + "gasmask, respirator, gas helmet", + "gas pump, gasoline pump, petrol pump, island dispenser", + "goblet", + "go-kart", + "golf ball", + "golfcart, golf cart", + "gondola", + "gong, tam-tam", + "gown", + "grand piano, grand", + "greenhouse, nursery, glasshouse", + "grille, radiator grille", + "grocery store, grocery, food market, market", + "guillotine", + "hair slide", + "hair spray", + "half track", + "hammer", + "hamper", + "hand blower, blow dryer, blow drier, hair dryer, hair drier", + "hand-held computer, hand-held microcomputer", + "handkerchief, hankie, hanky, hankey", + "hard disc, hard disk, fixed disk", + "harmonica, mouth organ, harp, mouth harp", + "harp", + "harvester, reaper", + "hatchet", + "holster", + "home theater, home theatre", + "honeycomb", + "hook, claw", + "hoopskirt, crinoline", + "horizontal bar, high bar", + "horse cart, horse-cart", + "hourglass", + "iPod", + "iron, smoothing iron", + "jack-o\"-lantern", + "jean, blue jean, denim", + "jeep, landrover", + "jersey, T-shirt, tee shirt", + "jigsaw puzzle", + "jinrikisha, ricksha, rickshaw", + "joystick", + "kimono", + "knee pad", + "knot", + "lab coat, laboratory coat", + "ladle", + "lampshade, lamp shade", + "laptop, laptop computer", + "lawn mower, mower", + "lens cap, lens cover", + "letter opener, paper knife, paperknife", + "library", + "lifeboat", + "lighter, light, igniter, ignitor", + "limousine, limo", + "liner, ocean liner", + "lipstick, lip rouge", + "Loafer", + "lotion", + "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system", + "loupe, jeweler\"s loupe", + "lumbermill, sawmill", + "magnetic compass", + "mailbag, postbag", + "mailbox, letter box", + "maillot", + "maillot, tank suit", + "manhole cover", + "maraca", + "marimba, xylophone", + "mask", + "matchstick", + "maypole", + "maze, labyrinth", + "measuring cup", + "medicine chest, medicine cabinet", + "megalith, megalithic structure", + "microphone, mike", + "microwave, microwave oven", + "military uniform", + "milk can", + "minibus", + "miniskirt, mini", + "minivan", + "missile", + "mitten", + "mixing bowl", + "mobile home, manufactured home", + "Model T", + "modem", + "monastery", + "monitor", + "moped", + "mortar", + "mortarboard", + "mosque", + "mosquito net", + "motor scooter, scooter", + "mountain bike, all-terrain bike, off-roader", + "mountain tent", + "mouse, computer mouse", + "mousetrap", + "moving van", + "muzzle", + "nail", + "neck brace", + "necklace", + "nipple", + "notebook, notebook computer", + "obelisk", + "oboe, hautboy, hautbois", + "ocarina, sweet potato", + "odometer, hodometer, mileometer, milometer", + "oil filter", + "organ, pipe organ", + "oscilloscope, scope, cathode-ray oscilloscope, CRO", + "overskirt", + "oxcart", + "oxygen mask", + "packet", + "paddle, boat paddle", + "paddlewheel, paddle wheel", + "padlock", + "paintbrush", + "pajama, pyjama, pj\"s, jammies", + "palace", + "panpipe, pandean pipe, syrinx", + "paper towel", + "parachute, chute", + "parallel bars, bars", + "park bench", + "parking meter", + "passenger car, coach, carriage", + "patio, terrace", + "pay-phone, pay-station", + "pedestal, plinth, footstall", + "pencil box, pencil case", + "pencil sharpener", + "perfume, essence", + "Petri dish", + "photocopier", + "pick, plectrum, plectron", + "pickelhaube", + "picket fence, paling", + "pickup, pickup truck", + "pier", + "piggy bank, penny bank", + "pill bottle", + "pillow", + "ping-pong ball", + "pinwheel", + "pirate, pirate ship", + "pitcher, ewer", + "plane, carpenter\"s plane, woodworking plane", + "planetarium", + "plastic bag", + "plate rack", + "plow, plough", + "plunger, plumber\"s helper", + "Polaroid camera, Polaroid Land camera", + "pole", + "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", + "poncho", + "pool table, billiard table, snooker table", + "pop bottle, soda bottle", + "pot, flowerpot", + "potter\"s wheel", + "power drill", + "prayer rug, prayer mat", + "printer", + "prison, prison house", + "projectile, missile", + "projector", + "puck, hockey puck", + "punching bag, punch bag, punching ball, punchball", + "purse", + "quill, quill pen", + "quilt, comforter, comfort, puff", + "racer, race car, racing car", + "racket, racquet", + "radiator", + "radio, wireless", + "radio telescope, radio reflector", + "rain barrel", + "recreational vehicle, RV, R.V.", + "reel", + "reflex camera", + "refrigerator, icebox", + "remote control, remote", + "restaurant, eating house, eating place, eatery", + "revolver, six-gun, six-shooter", + "rifle", + "rocking chair, rocker", + "rotisserie", + "rubber eraser, rubber, pencil eraser", + "rugby ball", + "rule, ruler", + "running shoe", + "safe", + "safety pin", + "saltshaker, salt shaker", + "sandal", + "sarong", + "sax, saxophone", + "scabbard", + "scale, weighing machine", + "school bus", + "schooner", + "scoreboard", + "screen, CRT screen", + "screw", + "screwdriver", + "seat belt, seatbelt", + "sewing machine", + "shield, buckler", + "shoe shop, shoe-shop, shoe store", + "shoji", + "shopping basket", + "shopping cart", + "shovel", + "shower cap", + "shower curtain", + "ski", + "ski mask", + "sleeping bag", + "slide rule, slipstick", + "sliding door", + "slot, one-armed bandit", + "snorkel", + "snowmobile", + "snowplow, snowplough", + "soap dispenser", + "soccer ball", + "sock", + "solar dish, solar collector, solar furnace", + "sombrero", + "soup bowl", + "space bar", + "space heater", + "space shuttle", + "spatula", + "speedboat", + "spider web, spider\"s web", + "spindle", + "sports car, sport car", + "spotlight, spot", + "stage", + "steam locomotive", + "steel arch bridge", + "steel drum", + "stethoscope", + "stole", + "stone wall", + "stopwatch, stop watch", + "stove", + "strainer", + "streetcar, tram, tramcar, trolley, trolley car", + "stretcher", + "studio couch, day bed", + "stupa, tope", + "submarine, pigboat, sub, U-boat", + "suit, suit of clothes", + "sundial", + "sunglass", + "sunglasses, dark glasses, shades", + "sunscreen, sunblock, sun blocker", + "suspension bridge", + "swab, swob, mop", + "sweatshirt", + "swimming trunks, bathing trunks", + "swing", + "switch, electric switch, electrical switch", + "syringe", + "table lamp", + "tank, army tank, armored combat vehicle, armoured combat vehicle", + "tape player", + "teapot", + "teddy, teddy bear", + "television, television system", + "tennis ball", + "thatch, thatched roof", + "theater curtain, theatre curtain", + "thimble", + "thresher, thrasher, threshing machine", + "throne", + "tile roof", + "toaster", + "tobacco shop, tobacconist shop, tobacconist", + "toilet seat", + "torch", + "totem pole", + "tow truck, tow car, wrecker", + "toyshop", + "tractor", + "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi", + "tray", + "trench coat", + "tricycle, trike, velocipede", + "trimaran", + "tripod", + "triumphal arch", + "trolleybus, trolley coach, trackless trolley", + "trombone", + "tub, vat", + "turnstile", + "typewriter keyboard", + "umbrella", + "unicycle, monocycle", + "upright, upright piano", + "vacuum, vacuum cleaner", + "vase", + "vault", + "velvet", + "vending machine", + "vestment", + "viaduct", + "violin, fiddle", + "volleyball", + "waffle iron", + "wall clock", + "wallet, billfold, notecase, pocketbook", + "wardrobe, closet, press", + "warplane, military plane", + "washbasin, handbasin, washbowl, lavabo, wash-hand basin", + "washer, automatic washer, washing machine", + "water bottle", + "water jug", + "water tower", + "whiskey jug", + "whistle", + "wig", + "window screen", + "window shade", + "Windsor tie", + "wine bottle", + "wing", + "wok", + "wooden spoon", + "wool, woolen, woollen", + "worm fence, snake fence, snake-rail fence, Virginia fence", + "wreck", + "yawl", + "yurt", + "web site, website, internet site, site", + "comic book", + "crossword puzzle, crossword", + "street sign", + "traffic light, traffic signal, stoplight", + "book jacket, dust cover, dust jacket, dust wrapper", + "menu", + "plate", + "guacamole", + "consomme", + "hot pot, hotpot", + "trifle", + "ice cream, icecream", + "ice lolly, lolly, lollipop, popsicle", + "French loaf", + "bagel, beigel", + "pretzel", + "cheeseburger", + "hotdog, hot dog, red hot", + "mashed potato", + "head cabbage", + "broccoli", + "cauliflower", + "zucchini, courgette", + "spaghetti squash", + "acorn squash", + "butternut squash", + "cucumber, cuke", + "artichoke, globe artichoke", + "bell pepper", + "cardoon", + "mushroom", + "Granny Smith", + "strawberry", + "orange", + "lemon", + "fig", + "pineapple, ananas", + "banana", + "jackfruit, jak, jack", + "custard apple", + "pomegranate", + "hay", + "carbonara", + "chocolate sauce, chocolate syrup", + "dough", + "meat loaf, meatloaf", + "pizza, pizza pie", + "potpie", + "burrito", + "red wine", + "espresso", + "cup", + "eggnog", + "alp", + "bubble", + "cliff, drop, drop-off", + "coral reef", + "geyser", + "lakeside, lakeshore", + "promontory, headland, head, foreland", + "sandbar, sand bar", + "seashore, coast, seacoast, sea-coast", + "valley, vale", + "volcano", + "ballplayer, baseball player", + "groom, bridegroom", + "scuba diver", + "rapeseed", + "daisy", + "yellow lady\"s slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", + "corn", + "acorn", + "hip, rose hip, rosehip", + "buckeye, horse chestnut, conker", + "coral fungus", + "agaric", + "gyromitra", + "stinkhorn, carrion fungus", + "earthstar", + "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa", + "bolete", + "ear, spike, capitulum", + "toilet tissue, toilet paper, bathroom tissue" +]; + + +// Sort the buffer of probabilities. The graph places the match probability for each class at the +// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert +// to a wrapping InferenceResult and sort the results. +pub fn sort_results(buffer: &[f32]) -> Vec { + let mut results: Vec = buffer + .iter() + .skip(1) + .enumerate() + .map(|(c, p)| InferenceResult{ index: c, weight: *p}) + .collect(); + results.sort_by(|a, b| b.weight.partial_cmp(&a.weight).unwrap()); + results +} + +// A wrapper for class ID and match probabilities. +#[derive(Debug, PartialEq)] +pub struct InferenceResult{ + pub index: usize, + pub weight: f32, +} diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 0cdd52776e..6663484a9e 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -23,8 +23,13 @@ mod ml { path: "wit/ml.wit" }); } +mod imagenet_classes; +use crate::imagenet_classes::{InferenceResult, sort_results}; + + use crate::ml::test::test::graph::ExecutionTarget; use crate::ml::test::test::graph::GraphEncoding; +use crate::ml::test::test::tensor::TensorType; use std::path::Path; use crate::hello::test::test::gggg2::say_hello; @@ -82,13 +87,13 @@ fn main() -> Result { let path_as_string = args.next().expect("path"); let path = Path::new(&path_as_string); let model: Vec = std::fs::read(&path.join("model.xml"))?; - println!("Loaded model from xml len = {}", model.len()); + eprintln!("Loaded model from xml len = {}", model.len()); let weights = std::fs::read(&path.join("model.bin"))?; - println!("Loaded weigths with len = {}", weights.len()); + eprintln!("Loaded weigths with len = {}", weights.len()); let imagenet_graph = ml::test::test::graph::load(&[model, weights], GraphEncoding::Openvino, ExecutionTarget::Cpu).unwrap(); - println!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); + eprintln!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); let context = ml::test::test::graph::Graph::init_execution_context(&imagenet_graph).unwrap(); - println!("Created context with ID: {:?}", context); + eprintln!("Created context with ID: {:?}", context); let tensor_dimensions:Vec = vec![1, 3, 224, 224]; let tensor_data = convert_image_to_tensor_bytes( "images/0.jpg", @@ -105,20 +110,31 @@ fn main() -> Result { let tensor_type = ml::test::test::tensor::TensorType::Fp32; let tensor_id = ml::test::test::tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); - println!("Created tensor with ID: {:?}", tensor_id); + eprintln!("Created tensor with ID: {:?}", tensor_id); let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); - println!("Input set with ID: {:?}", set_input_result); + eprintln!("Input set with ID: {:?}", set_input_result); let infered_result = ml::test::test::inference::GraphExecutionContext::compute(&context).unwrap(); - println!("Executed graph inference"); + eprintln!("Executed graph inference"); let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0").unwrap(); - let output_result = ml::test::test::tensor::Tensor::data(&output_result_id); - println!("output = {:?}", &output_result); - - - println!("Kuku!"); + let output_data = ml::test::test::tensor::Tensor::data(&output_result_id); + let output_dimensions = ml::test::test::tensor::Tensor::dimensions(&output_result_id); + let output_type = ml::test::test::tensor::Tensor::ty(&output_result_id); + if output_dimensions.len() == 2 && output_dimensions[0] == 1 && output_dimensions[1] == 1001 && output_type == TensorType::Fp32 { + let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; + let results = sort_results(&output_vec_f32); + for i in 0..3 { + println!( + "{:.2} -> {}", + results[i].weight, + imagenet_classes::IMAGENET_CLASSES[results[i].index], + ); + } + } else { + eprintln!("Output not as expected, output = {:?} {:?}", &output_dimensions, &output_type); + } } "sleep" => { let duration = @@ -133,4 +149,4 @@ fn main() -> Result { cmd => panic!("unknown cmd {cmd}"), }; Ok(()) -} +} \ No newline at end of file diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index e1aebabf18..88eceefdfd 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -8,7 +8,7 @@ mod hello_component; mod ml_component; use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; -use crate::ml_component::ml::{MLHostComponent, MLHostImpl}; +use crate::ml_component::ml::{MLHostComponent}; use anyhow::Context; use spin_core::{ @@ -203,13 +203,12 @@ async fn test_host_component_imagenet() { ["imagenet", "/"], |store_builder| { store_builder.read_only_preopened_dir(&imagenet_path, "/".into()).unwrap(); - //probably no needed! store_builder.host_components_data().set(handle, MLHostImpl {..Default::default()}); }, |_| {}, ) .await .unwrap(); - assert_eq!(stdout, "Hello bace GGyci!"); + assert_eq!(stdout, "0.47 -> Eskimo dog, husky\n0.37 -> Siberian husky\n0.01 -> malamute, malemute, Alaskan malamute"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index cdcf03456d..9762b4cee8 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -2,7 +2,7 @@ pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); - use anyhow::Ok; + use spin_core::HostComponent; use anyhow::{anyhow, Context}; @@ -18,7 +18,7 @@ pub mod ml { use tokio::sync::Mutex; use table; - use openvino::{Core, InferenceError, Layout, Precision, SetupError, TensorDesc, InferRequest}; + use openvino::{Layout, Precision, TensorDesc}; #[derive(Clone)] pub struct MLHostComponent; @@ -108,7 +108,7 @@ pub mod ml { }; let mut exec_network = self.openvino.as_mut().expect("").load_network(&cnn_network, map_execution_target_to_string(graph.target))?; - let infer_request = exec_network.create_infer_request().unwrap(); + let infer_request = exec_network.create_infer_request().expect("Can't create InferRequest"); let graph_execution_context = GraphExecutionContextInternalData { cnn_network: cnn_network, executable_network: Mutex::new(exec_network), @@ -116,44 +116,28 @@ pub mod ml { }; match self.execution_contexts.push(graph_execution_context).map(Resource::::new_own) { - std::result::Result::Ok(res) => { - return std::result::Result::Ok(std::result::Result::Ok(res)); + Ok(res) => { + return Ok(Ok(res)); } Err(_) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: "Can't create graph execution context".to_string()}){ - std::result::Result::Ok(id) => { - return std::result::Result::Ok(Err(Resource::::new_own(id))); + Ok(id) => { + return Ok(Err(Resource::::new_own(id))); } Err(_) => { return Err(anyhow!("Can't allocate error")); } } - //.map(Resource::::new_own) - // .map_err(|_|anyhow!("Can't allocate error"))); - - //return std::result::Result::Ok(Err(err)); } } - /* - if let std::result::Result::Ok(graph_execution_context_id) = - self.execution_contexts.push(graph_execution_context) - .map(Resource::::new_own) - - { - println!("graph_execution_context_id = {:?}", graph_execution_context_id); - return std::result::Result::Ok(std::result::Result::Ok(graph_execution_context_id)) - }; - */ } } Err(anyhow!("[graph::HostGraph] fn init_execution_context -> Not implemented")) } fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { - let v = self.graphs.remove(graph.rep()); - //println!("v = {v:?}"); - v.ok_or(anyhow!(format!("Can't find graph with ID = {}", graph.rep()))).map(|_| ()) - + let _v = self.graphs.remove(graph.rep()); + Ok(()) } } @@ -235,12 +219,12 @@ pub mod ml { let input_name = execution_context.cnn_network.get_input_name(index).context(format!("Can't find input with name = {}", index))?; match execution_context.infer_request.set_blob(&input_name, &blob) { - std::result::Result::Ok(res) => { - return std::result::Result::Ok(std::result::Result::Ok(res)); + Ok(res) => { + return Ok(Ok(res)); } std::result::Result::Err(err) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - std::result::Result::Ok(error_rep) => { + Ok(error_rep) => { return Ok(Err(Resource::::new_own(error_rep))); } Err(err) => { @@ -259,10 +243,10 @@ pub mod ml { .get_mut(graph_execution_context.rep()) .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; match graph_execution.infer_request.infer() { - std::result::Result::Ok(..) => { return std::result::Result::Ok(std::result::Result::Ok(())); } + Ok(..) => { return Ok(Ok(())); } Err(err) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - std::result::Result::Ok(error_rep) => { + Ok(error_rep) => { return Ok(Err(Resource::::new_own(error_rep))); } Err(err) => { @@ -285,10 +269,8 @@ pub mod ml { .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; let output_name = graph_execution.cnn_network.get_output_name(index).context("Can't find output name for ID = {index}")?; let blob = graph_execution.infer_request.get_blob(&output_name).context("Can't get blob for output name = {output_name}")?; - //let blob_size = blob.byte_len()?; let tensor_desc = blob.tensor_desc().context("Can't get blob description")?; let buffer = blob.buffer() - //let buffer = blob.buffer_as_type::() .context("Can't get blob buffer")?.iter() .map(|&d| d as u8) .collect::>(); @@ -302,12 +284,12 @@ pub mod ml { tensor_data: buffer, }; match self.tensors.push(tensor).map(Resource::::new_own) { - std::result::Result::Ok(t) => { - return std::result::Result::Ok(std::result::Result::Ok(t)); + Ok(t) => { + return Ok(Ok(t)); } Err(err) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - std::result::Result::Ok(error_rep) => { + Ok(error_rep) => { return Ok(Err(Resource::::new_own(error_rep))); } Err(err) => { @@ -347,12 +329,12 @@ pub mod ml { // Read the guest array. let graph_internal_data = GraphInternalData { xml: graph[0].clone(), weights: graph[1].clone(), target: target }; match self.graphs.push(graph_internal_data) { - std::result::Result::Ok(graph_rep) => { - return std::result::Result::Ok(std::result::Result::Ok(Resource::::new_own(graph_rep))); + Ok(graph_rep) => { + return Ok(Ok(Resource::::new_own(graph_rep))); }, Err(err) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - std::result::Result::Ok(error_rep) => { + Ok(error_rep) => { return Ok(Err(Resource::::new_own(error_rep))); } Err(err) => { @@ -361,14 +343,6 @@ pub mod ml { } } } - /*/ - if let std::result::Result::Ok(graph_id) = self.graphs.push(graph_internal_data).map(Resource::::new_own){ - println!("{:?}", graph_id); - return std::result::Result::Ok(std::result::Result::Ok(graph_id)) - };//.ok_or(sqlite::Error::InvalidConnection); - - Err(anyhow!("[graph::Host] fn load -> Not implemented")) - */ } fn load_by_name( &mut self, From 1e9b49dd06f9224374f759f086eb6ca27e56d9c4 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 12 Jun 2024 15:07:37 +0300 Subject: [PATCH 13/26] Clean up some code --- crates/core/tests/core-wasi-test/src/main.rs | 18 +++++- crates/core/tests/ml_component.rs | 58 +++++++------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 6663484a9e..47752fdeae 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -39,6 +39,15 @@ use image2tensor::convert_image_to_tensor_bytes; type Result = std::result::Result<(), Box>; +fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { + if elapsed < 1000 { + format!("`{}` took {} ns", fn_name, elapsed) + } else if elapsed < 1000 * 1000 { + format!("`{}` took {:.2} µs", fn_name, elapsed as f64 / 1000.0) + } else { + format!("`{}` took {:.2} ms", fn_name, elapsed as f64 / 1000.0 / 1000.0) + } +} fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); @@ -90,9 +99,11 @@ fn main() -> Result { eprintln!("Loaded model from xml len = {}", model.len()); let weights = std::fs::read(&path.join("model.bin"))?; eprintln!("Loaded weigths with len = {}", weights.len()); - let imagenet_graph = ml::test::test::graph::load(&[model, weights], GraphEncoding::Openvino, ExecutionTarget::Cpu).unwrap(); + let imagenet_graph = ml::test::test::graph::load(&[model, weights], GraphEncoding::Openvino, ExecutionTarget::Gpu).unwrap(); eprintln!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); + let context = ml::test::test::graph::Graph::init_execution_context(&imagenet_graph).unwrap(); + eprintln!("Created context with ID: {:?}", context); let tensor_dimensions:Vec = vec![1, 3, 224, 224]; let tensor_data = convert_image_to_tensor_bytes( @@ -114,9 +125,10 @@ fn main() -> Result { let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); eprintln!("Input set with ID: {:?}", set_input_result); - + let start_for_elapsed_macro = std::time::Instant::now(); let infered_result = ml::test::test::inference::GraphExecutionContext::compute(&context).unwrap(); - eprintln!("Executed graph inference"); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Executed graph inference. {}", elapsed_to_string("compute", elapsed)); let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0").unwrap(); let output_data = ml::test::test::tensor::Tensor::data(&output_result_id); diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 9762b4cee8..8e3fa0c1f7 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -7,6 +7,7 @@ pub mod ml { use anyhow::{anyhow, Context}; use test::test::errors; + use test::test::errors::HostError; use test::test::graph; use test::test::inference; use test::test::tensor; @@ -66,7 +67,7 @@ pub mod ml { pub struct MLHostImpl { pub openvino: Option, pub graphs: table::Table, - pub execution_contexts: table::Table, + pub executions: table::Table, pub tensors: table::Table, pub errors: table::Table, @@ -115,7 +116,7 @@ pub mod ml { infer_request: infer_request, }; - match self.execution_contexts.push(graph_execution_context).map(Resource::::new_own) { + match self.executions.push(graph_execution_context).map(Resource::::new_own) { Ok(res) => { return Ok(Ok(res)); } @@ -215,44 +216,30 @@ pub mod ml { .collect::>(); let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data)?; - let execution_context: &mut GraphExecutionContextInternalData = self.execution_contexts.get_mut(graph_execution_context.rep()).context(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep()))?; - + let execution_context: &mut GraphExecutionContextInternalData = self.executions.get_mut(graph_execution_context.rep()).context(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep()))?; let input_name = execution_context.cnn_network.get_input_name(index).context(format!("Can't find input with name = {}", index))?; - match execution_context.infer_request.set_blob(&input_name, &blob) { + let res = match execution_context.infer_request.set_blob(&input_name, &blob) { Ok(res) => { - return Ok(Ok(res)); + Ok(res) } - std::result::Result::Err(err) => { - match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - Ok(error_rep) => { - return Ok(Err(Resource::::new_own(error_rep))); - } - Err(err) => { - return Err(anyhow!("Can't create internal error for {:?}", err)); - } - } + Err(err) => { + Err(self.new(ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()))?) } - } + }; + Ok(res) } fn compute( &mut self, graph_execution_context: Resource, ) -> Result>, anyhow::Error> { - let graph_execution = self.execution_contexts + let graph_execution = self.executions .get_mut(graph_execution_context.rep()) .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; match graph_execution.infer_request.infer() { - Ok(..) => { return Ok(Ok(())); } + Ok(..) => { Ok(Ok(())) } Err(err) => { - match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - Ok(error_rep) => { - return Ok(Err(Resource::::new_own(error_rep))); - } - Err(err) => { - return Err(anyhow!("Can't create internal error for {:?}", err)); - } - } + Ok(Err(self.new(ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()))?)) } } } @@ -264,7 +251,7 @@ pub mod ml { ) -> Result, Resource>, anyhow::Error> { let index = input_name.parse::().context("Can't parse {} to usize for input_name")?; - let graph_execution = self.execution_contexts + let graph_execution = self.executions .get_mut(graph_execution_context.rep()) .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; let output_name = graph_execution.cnn_network.get_output_name(index).context("Can't find output name for ID = {index}")?; @@ -283,21 +270,14 @@ pub mod ml { tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), tensor_data: buffer, }; - match self.tensors.push(tensor).map(Resource::::new_own) { + Ok(match self.tensors.push(tensor).map(Resource::::new_own) { Ok(t) => { - return Ok(Ok(t)); + Ok(t) } - Err(err) => { - match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { - Ok(error_rep) => { - return Ok(Err(Resource::::new_own(error_rep))); - } - Err(err) => { - return Err(anyhow!("Can't create internal error for {:?}", err)); - } - } + Err(_) => { + Err(self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))?) } - } + }) } fn drop( From 9ee8887510614e91945fd2d3e859fd54fcf516a8 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 12 Jun 2024 15:18:36 +0300 Subject: [PATCH 14/26] Double free fixed --- crates/core/tests/ml_component.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 8e3fa0c1f7..c65652a2c3 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -136,8 +136,9 @@ pub mod ml { } Err(anyhow!("[graph::HostGraph] fn init_execution_context -> Not implemented")) } + fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { - let _v = self.graphs.remove(graph.rep()); + self.graphs.remove(graph.rep()).context(format!("Can't find graph with ID = {}", graph.rep()))?; Ok(()) } } @@ -194,7 +195,8 @@ pub mod ml { self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_data.clone()) } fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { - self.tensors.remove(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|_| ()) + self.tensors.remove(tensor.rep()).context(format!("Can't find tensor with ID = {}", tensor.rep()))?; + Ok(()) } } @@ -270,22 +272,19 @@ pub mod ml { tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), tensor_data: buffer, }; - Ok(match self.tensors.push(tensor).map(Resource::::new_own) { + match self.tensors.push(tensor).map(Resource::::new_own) { Ok(t) => { - Ok(t) + return Ok(Ok(t)); } Err(_) => { - Err(self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))?) + return Ok(Err(self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))?)) } - }) + } } - fn drop( - &mut self, - graph_execution_context: Resource, - ) -> Result<(), anyhow::Error> { - let id = graph_execution_context.rep(); - self.graphs.remove(id).context("{Can't drow GraphExecutionContext with id = {id}")?; + fn drop(&mut self, execution: Resource) -> Result<(), anyhow::Error> { + let id = execution.rep(); + self.executions.remove(id).context("{Can't drow GraphExecutionContext with id = {id}")?; Ok(()) } From 8492b4a5178899a707b286658ad588cb64c42e95 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 12 Jun 2024 15:52:08 +0300 Subject: [PATCH 15/26] Move code outside main test block --- .../core/tests/core-wasi-test/src/imagenet.rs | 105 ++++++++++++++++++ .../core-wasi-test/src/imagenet_classes.rs | 20 ---- crates/core/tests/core-wasi-test/src/main.rs | 76 ++----------- 3 files changed, 113 insertions(+), 88 deletions(-) create mode 100644 crates/core/tests/core-wasi-test/src/imagenet.rs diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs new file mode 100644 index 0000000000..a26a3f0610 --- /dev/null +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -0,0 +1,105 @@ + + +//use crate::ml::test::test::graph::ExecutionTarget; +//use crate::ml::test::test::graph::GraphEncoding; + +//use crate::ml::test::test::tensor::TensorType; +//use crate::ml::test::test::tensor::Tensor; +//use crate::ml::test::test::inference::Tensor; +use crate::ml::test::test::{graph, tensor, inference}; + + +use image2tensor; +use image2tensor::convert_image_to_tensor_bytes; + +use crate::Path; +use crate::imagenet_classes::IMAGENET_CLASSES; +use crate::imagenet_classes; + +pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { + if elapsed < 1000 { + format!("`{}` took {} ns", fn_name, elapsed) + } else if elapsed < 1000 * 1000 { + format!("`{}` took {:.2} µs", fn_name, elapsed as f64 / 1000.0) + } else { + format!("`{}` took {:.2} ms", fn_name, elapsed as f64 / 1000.0 / 1000.0) + } +} + +pub fn imagenet_openvino_test(path_as_string: String) -> std::result::Result<(), Box> { + + let path = Path::new(&path_as_string); + let model: Vec = std::fs::read(&path.join("model.xml"))?; + eprintln!("Loaded model from xml len = {}", model.len()); + let weights = std::fs::read(&path.join("model.bin"))?; + eprintln!("Loaded weigths with len = {}", weights.len()); + let imagenet_graph = graph::load(&[model, weights], graph::GraphEncoding::Openvino, graph::ExecutionTarget::Gpu).unwrap(); + eprintln!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); + + let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); + eprintln!("Created context with ID: {:?}", context); + + let tensor_dimensions:Vec = vec![1, 3, 224, 224]; + let tensor_data = convert_image_to_tensor_bytes( + "images/0.jpg", + tensor_dimensions[2], + tensor_dimensions[3], + image2tensor::TensorType::F32, + image2tensor::ColorOrder::BGR, + ) + .or_else(|e| Err(e)) + .unwrap(); + + + + let tensor_type = tensor::TensorType::Fp32; + let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); + eprintln!("Created tensor with ID: {:?}", tensor_id); + + let set_input_result = inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); + eprintln!("Input set with ID: {:?}", set_input_result); + let start_for_elapsed_macro = std::time::Instant::now(); + let infered_result = inference::GraphExecutionContext::compute(&context).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Executed graph inference. {}", elapsed_to_string("compute", elapsed)); + let output_result_id = inference::GraphExecutionContext::get_output(&context, "0").unwrap(); + + let output_data = tensor::Tensor::data(&output_result_id); + let output_dimensions = tensor::Tensor::dimensions(&output_result_id); + let output_type = tensor::Tensor::ty(&output_result_id); + if output_dimensions.len() == 2 && output_dimensions[0] == 1 && output_dimensions[1] == 1001 && output_type == tensor::TensorType::Fp32 { + let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; + let results = sort_results(&output_vec_f32); + for i in 0..3 { + println!( + "{:.2} -> {}", + results[i].weight, + imagenet_classes::IMAGENET_CLASSES[results[i].index], + ); + } + } else { + eprintln!("Output not as expected, output = {:?} {:?}", &output_dimensions, &output_type); + } + Ok(()) +} + +// Sort the buffer of probabilities. The graph places the match probability for each class at the +// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert +// to a wrapping InferenceResult and sort the results. +pub fn sort_results(buffer: &[f32]) -> Vec { + let mut results: Vec = buffer + .iter() + .skip(1) + .enumerate() + .map(|(c, p)| InferenceResult{ index: c, weight: *p}) + .collect(); + results.sort_by(|a, b| b.weight.partial_cmp(&a.weight).unwrap()); + results +} + +// A wrapper for class ID and match probabilities. +#[derive(Debug, PartialEq)] +pub struct InferenceResult{ + pub index: usize, + pub weight: f32, +} diff --git a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs index 6081390e51..2ab1ed9bcb 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs @@ -1021,23 +1021,3 @@ pub const IMAGENET_CLASSES: [&str; 1000] = [ ]; -// Sort the buffer of probabilities. The graph places the match probability for each class at the -// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert -// to a wrapping InferenceResult and sort the results. -pub fn sort_results(buffer: &[f32]) -> Vec { - let mut results: Vec = buffer - .iter() - .skip(1) - .enumerate() - .map(|(c, p)| InferenceResult{ index: c, weight: *p}) - .collect(); - results.sort_by(|a, b| b.weight.partial_cmp(&a.weight).unwrap()); - results -} - -// A wrapper for class ID and match probabilities. -#[derive(Debug, PartialEq)] -pub struct InferenceResult{ - pub index: usize, - pub weight: f32, -} diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 47752fdeae..6ac4a5a06d 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -23,31 +23,23 @@ mod ml { path: "wit/ml.wit" }); } + mod imagenet_classes; -use crate::imagenet_classes::{InferenceResult, sort_results}; +mod imagenet; + +use imagenet::imagenet_openvino_test; + -use crate::ml::test::test::graph::ExecutionTarget; -use crate::ml::test::test::graph::GraphEncoding; -use crate::ml::test::test::tensor::TensorType; use std::path::Path; use crate::hello::test::test::gggg2::say_hello; -use image2tensor; -use image2tensor::convert_image_to_tensor_bytes; + type Result = std::result::Result<(), Box>; -fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { - if elapsed < 1000 { - format!("`{}` took {} ns", fn_name, elapsed) - } else if elapsed < 1000 * 1000 { - format!("`{}` took {:.2} µs", fn_name, elapsed as f64 / 1000.0) - } else { - format!("`{}` took {:.2} ms", fn_name, elapsed as f64 / 1000.0 / 1000.0) - } -} + fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); @@ -94,59 +86,7 @@ fn main() -> Result { } "imagenet" => { let path_as_string = args.next().expect("path"); - let path = Path::new(&path_as_string); - let model: Vec = std::fs::read(&path.join("model.xml"))?; - eprintln!("Loaded model from xml len = {}", model.len()); - let weights = std::fs::read(&path.join("model.bin"))?; - eprintln!("Loaded weigths with len = {}", weights.len()); - let imagenet_graph = ml::test::test::graph::load(&[model, weights], GraphEncoding::Openvino, ExecutionTarget::Gpu).unwrap(); - eprintln!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); - - let context = ml::test::test::graph::Graph::init_execution_context(&imagenet_graph).unwrap(); - - eprintln!("Created context with ID: {:?}", context); - let tensor_dimensions:Vec = vec![1, 3, 224, 224]; - let tensor_data = convert_image_to_tensor_bytes( - "images/0.jpg", - tensor_dimensions[2], - tensor_dimensions[3], - image2tensor::TensorType::F32, - image2tensor::ColorOrder::BGR, - ) - .or_else(|e| Err(e)) - .unwrap(); - - - - - let tensor_type = ml::test::test::tensor::TensorType::Fp32; - let tensor_id = ml::test::test::tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); - eprintln!("Created tensor with ID: {:?}", tensor_id); - - let set_input_result = ml::test::test::inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); - eprintln!("Input set with ID: {:?}", set_input_result); - let start_for_elapsed_macro = std::time::Instant::now(); - let infered_result = ml::test::test::inference::GraphExecutionContext::compute(&context).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Executed graph inference. {}", elapsed_to_string("compute", elapsed)); - let output_result_id = ml::test::test::inference::GraphExecutionContext::get_output(&context, "0").unwrap(); - - let output_data = ml::test::test::tensor::Tensor::data(&output_result_id); - let output_dimensions = ml::test::test::tensor::Tensor::dimensions(&output_result_id); - let output_type = ml::test::test::tensor::Tensor::ty(&output_result_id); - if output_dimensions.len() == 2 && output_dimensions[0] == 1 && output_dimensions[1] == 1001 && output_type == TensorType::Fp32 { - let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; - let results = sort_results(&output_vec_f32); - for i in 0..3 { - println!( - "{:.2} -> {}", - results[i].weight, - imagenet_classes::IMAGENET_CLASSES[results[i].index], - ); - } - } else { - eprintln!("Output not as expected, output = {:?} {:?}", &output_dimensions, &output_type); - } + _ = imagenet_openvino_test(path_as_string); } "sleep" => { let duration = From d24a59cce3288124a6f9ecaf90b7700320cffe99 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 12 Jun 2024 18:45:01 +0300 Subject: [PATCH 16/26] Nicer arguments --- .../core/tests/core-wasi-test/src/imagenet.rs | 129 +++++--- crates/core/tests/core-wasi-test/src/main.rs | 5 +- crates/core/tests/integration_test.rs | 43 ++- crates/core/tests/ml_component.rs | 275 +++++++++++++----- 4 files changed, 328 insertions(+), 124 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs index a26a3f0610..6d535451bb 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -1,19 +1,10 @@ -//use crate::ml::test::test::graph::ExecutionTarget; -//use crate::ml::test::test::graph::GraphEncoding; - -//use crate::ml::test::test::tensor::TensorType; -//use crate::ml::test::test::tensor::Tensor; -//use crate::ml::test::test::inference::Tensor; use crate::ml::test::test::{graph, tensor, inference}; - - use image2tensor; use image2tensor::convert_image_to_tensor_bytes; use crate::Path; -use crate::imagenet_classes::IMAGENET_CLASSES; use crate::imagenet_classes; pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { @@ -26,22 +17,65 @@ pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { } } -pub fn imagenet_openvino_test(path_as_string: String) -> std::result::Result<(), Box> { - +pub fn bytes_to_string(b: usize) -> String { + if b < 1024 { + format!("{} Bytes", b) + } else if b < 1024 * 1024 { + format!("{:.2} kB", b as f64 / 1024.0) + } else { + format!("{:.2} MB", b as f64 / 1024.0 / 1024.0) + } +} + +/// Return the execution target type from string +fn map_string_to_execution_target(target: &str) -> Result { + match target { + "CPU" => Ok(graph::ExecutionTarget::Cpu), + "GPU" => Ok(graph::ExecutionTarget::Gpu), + "TPU" => Ok(graph::ExecutionTarget::Tpu), + _ => { + Err(format!("Unknown execution targer = {}", target)) + } + } +} + + +pub fn imagenet_openvino_test(path_as_string: String, target_as_string: String, image_file: String) -> std::result::Result<(), Box> { let path = Path::new(&path_as_string); - let model: Vec = std::fs::read(&path.join("model.xml"))?; - eprintln!("Loaded model from xml len = {}", model.len()); - let weights = std::fs::read(&path.join("model.bin"))?; - eprintln!("Loaded weigths with len = {}", weights.len()); - let imagenet_graph = graph::load(&[model, weights], graph::GraphEncoding::Openvino, graph::ExecutionTarget::Gpu).unwrap(); - eprintln!("Loaded graph into wasi-nn with ID: {:?}", imagenet_graph); - - let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); - eprintln!("Created context with ID: {:?}", context); + let target = map_string_to_execution_target(&target_as_string)?; + let model = { + let start_for_elapsed_macro = std::time::Instant::now(); + let model: Vec = std::fs::read(&path.join("model.xml"))?; + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Loaded model from xml {} {}", bytes_to_string(model.len()), elapsed_to_string("fs::read", elapsed)); + model + }; + let weights = { + let start_for_elapsed_macro = std::time::Instant::now(); + let weights = std::fs::read(&path.join("model.bin"))?; + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Loaded weigths {} {}", bytes_to_string(weights.len()), elapsed_to_string("fs::read", elapsed)); + weights + }; + let imagenet_graph = { + let start_for_elapsed_macro = std::time::Instant::now(); + let imagenet_graph = graph::load(&[model, weights], graph::GraphEncoding::Openvino, target).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("---- {:?} ----", target); + eprintln!("Loaded graph with ID: {:?} {}", imagenet_graph, elapsed_to_string("graph::load", elapsed)); + imagenet_graph + }; + let context = { + let start_for_elapsed_macro = std::time::Instant::now(); + let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Created context with ID: {:?} {}", context, elapsed_to_string("Graph::init_execution_context", elapsed)); + context + }; let tensor_dimensions:Vec = vec![1, 3, 224, 224]; let tensor_data = convert_image_to_tensor_bytes( - "images/0.jpg", + &image_file,//"images/0.jpg", tensor_dimensions[2], tensor_dimensions[3], image2tensor::TensorType::F32, @@ -51,22 +85,43 @@ pub fn imagenet_openvino_test(path_as_string: String) -> std::result::Result<(), .unwrap(); - - let tensor_type = tensor::TensorType::Fp32; - let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); - eprintln!("Created tensor with ID: {:?}", tensor_id); - - let set_input_result = inference::GraphExecutionContext::set_input(&context, "0", tensor_id).unwrap(); - eprintln!("Input set with ID: {:?}", set_input_result); - let start_for_elapsed_macro = std::time::Instant::now(); - let infered_result = inference::GraphExecutionContext::compute(&context).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Executed graph inference. {}", elapsed_to_string("compute", elapsed)); - let output_result_id = inference::GraphExecutionContext::get_output(&context, "0").unwrap(); - - let output_data = tensor::Tensor::data(&output_result_id); - let output_dimensions = tensor::Tensor::dimensions(&output_result_id); - let output_type = tensor::Tensor::ty(&output_result_id); + let tensor_id = { + let start_for_elapsed_macro = std::time::Instant::now(); + let tensor_type = tensor::TensorType::Fp32; + let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Created tensor with ID: {:?} {}", tensor_id, elapsed_to_string("Tensor::new", elapsed)); + tensor_id + }; + let input_name = "0"; + { + let start_for_elapsed_macro = std::time::Instant::now(); + let set_input_result = inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Input set with ID: {:?} {}", set_input_result, elapsed_to_string("GraphExecutionContext::set_input", elapsed)); + } + { + let start_for_elapsed_macro = std::time::Instant::now(); + let _infered_result = inference::GraphExecutionContext::compute(&context).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Executed graph inference. {}", elapsed_to_string("GraphExecutionContext::compute", elapsed)); + } + let output_result_id = { + let start_for_elapsed_macro = std::time::Instant::now(); + let output_result_id = inference::GraphExecutionContext::get_output(&context, input_name).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Obtaining output {}", elapsed_to_string("GraphExecutionContext::get_output", elapsed)); + output_result_id + }; + let (output_data, output_dimensions, output_type) = { + let start_for_elapsed_macro = std::time::Instant::now(); + let output_data = tensor::Tensor::data(&output_result_id); + let output_dimensions = tensor::Tensor::dimensions(&output_result_id); + let output_type = tensor::Tensor::ty(&output_result_id); + let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + eprintln!("Copying data from tensor. {}", elapsed_to_string("Tensor::data+dimensions+type", elapsed)); + (output_data, output_dimensions, output_type) + }; if output_dimensions.len() == 2 && output_dimensions[0] == 1 && output_dimensions[1] == 1001 && output_type == tensor::TensorType::Fp32 { let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; let results = sort_results(&output_vec_f32); diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 6ac4a5a06d..93cbb7010e 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -86,7 +86,10 @@ fn main() -> Result { } "imagenet" => { let path_as_string = args.next().expect("path"); - _ = imagenet_openvino_test(path_as_string); + let target_as_string = args.next().expect("target"); + let image_file_as_string = args.next().expect("image_file"); + _ = imagenet_openvino_test(path_as_string, target_as_string, image_file_as_string); + } "sleep" => { let duration = diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 88eceefdfd..81262e3605 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -8,7 +8,7 @@ mod hello_component; mod ml_component; use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; -use crate::ml_component::ml::{MLHostComponent}; +use crate::ml_component::ml::MLHostComponent; use anyhow::Context; use spin_core::{ @@ -17,7 +17,6 @@ use spin_core::{ use tempfile::TempDir; use tokio::{fs, io::AsyncWrite}; - #[tokio::test(flavor = "multi_thread")] async fn test_stdio() { let stdout = run_core_wasi_test(["echo"], |store_builder| { @@ -167,7 +166,7 @@ async fn test_host_component_data_update() { } #[tokio::test(flavor = "multi_thread")] -async fn test_host_component_nn() { +async fn test_host_component_hello() { let engine = test_engine(); let hello_handle = engine .find_host_component_handle::() @@ -189,20 +188,22 @@ async fn test_host_component_nn() { } #[tokio::test(flavor = "multi_thread")] -async fn test_host_component_imagenet() { +async fn test_host_component_imagenet_openvino_cpu() { let engine = test_engine(); let _handle = engine .find_host_component_handle::() .unwrap(); - let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../target/test-programs/imagenet"); + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); println!("imagenet_path = {imagenet_path:?}"); let stdout = run_core_wasi_test_engine( &engine, - ["imagenet", "/"], + ["imagenet", "/", "CPU", "images/0.jpg"], |store_builder| { - store_builder.read_only_preopened_dir(&imagenet_path, "/".into()).unwrap(); + store_builder + .read_only_preopened_dir(&imagenet_path, "/".into()) + .unwrap(); }, |_| {}, ) @@ -211,6 +212,32 @@ async fn test_host_component_imagenet() { assert_eq!(stdout, "0.47 -> Eskimo dog, husky\n0.37 -> Siberian husky\n0.01 -> malamute, malemute, Alaskan malamute"); } +#[tokio::test(flavor = "multi_thread")] +async fn test_host_component_imagenet_openvino_gpu() { + let engine = test_engine(); + let _handle = engine + .find_host_component_handle::() + .unwrap(); + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + println!("imagenet_path = {imagenet_path:?}"); + + let stdout = run_core_wasi_test_engine( + &engine, + ["imagenet", "/", "GPU", "images/1.jpg"], + |store_builder| { + store_builder + .read_only_preopened_dir(&imagenet_path, "/".into()) + .unwrap(); + }, + |_| {}, + ) + .await + .unwrap(); + assert_eq!(stdout, "0.96 -> mountain bike, all-terrain bike, off-roader\n0.01 -> bicycle-built-for-two, tandem bicycle, tandem\n0.00 -> alp"); +} + + #[tokio::test(flavor = "multi_thread")] #[cfg(not(tarpaulin))] async fn test_panic() { diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index c65652a2c3..19456cac0f 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -1,8 +1,6 @@ - pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); - use spin_core::HostComponent; use anyhow::{anyhow, Context}; @@ -15,11 +13,11 @@ pub mod ml { use test::test::errors::ErrorCode; use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; use test::test::inference::GraphExecutionContext; - use wasmtime::component::Resource; use tokio::sync::Mutex; - - use table; + use wasmtime::component::Resource; + use openvino::{Layout, Precision, TensorDesc}; + use table; #[derive(Clone)] pub struct MLHostComponent; @@ -35,7 +33,9 @@ pub mod ml { } fn build_data(&self) -> Self::Data { - MLHostImpl { ..Default::default()} + MLHostImpl { + ..Default::default() + } } } #[derive(Debug)] @@ -45,7 +45,6 @@ pub mod ml { pub target: ExecutionTarget, } - pub struct GraphExecutionContextInternalData { pub cnn_network: openvino::CNNNetwork, pub executable_network: Mutex, @@ -70,7 +69,6 @@ pub mod ml { pub executions: table::Table, pub tensors: table::Table, pub errors: table::Table, - } impl graph::HostGraph for MLHostImpl { @@ -88,10 +86,14 @@ pub mod ml { if self.openvino.is_none() { self.openvino.replace(openvino::Core::new(None)?); } - if self.openvino.is_some() { + if self.openvino.is_some() { if let Some(graph) = self.graphs.get(graph.rep()) { - let mut cnn_network = self.openvino.as_mut().expect("").read_network_from_buffer(&graph.xml, &graph.weights)?; - + let mut cnn_network = self + .openvino + .as_mut() + .expect("") + .read_network_from_buffer(&graph.xml, &graph.weights)?; + // Construct OpenVINO graph structures: `cnn_network` contains the graph // structure, `exec_network` can perform inference. //let core = self @@ -99,29 +101,42 @@ pub mod ml { // .as_mut() // .expect("openvino::Core was previously constructed"); //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; - + // TODO: this is a temporary workaround. We need a more elegant way to // specify the layout in the long run. However, without this newer // versions of OpenVINO will fail due to parameter mismatch. for i in 0..cnn_network.get_inputs_len().unwrap() { let name = cnn_network.get_input_name(i)?; cnn_network.set_input_layout(&name, Layout::NHWC)?; - }; - - let mut exec_network = self.openvino.as_mut().expect("").load_network(&cnn_network, map_execution_target_to_string(graph.target))?; - let infer_request = exec_network.create_infer_request().expect("Can't create InferRequest"); + } + + let mut exec_network = self + .openvino + .as_mut() + .expect("") + .load_network(&cnn_network, map_execution_target_to_string(graph.target))?; + let infer_request = exec_network + .create_infer_request() + .expect("Can't create InferRequest"); let graph_execution_context = GraphExecutionContextInternalData { - cnn_network: cnn_network, + cnn_network: cnn_network, executable_network: Mutex::new(exec_network), infer_request: infer_request, }; - match self.executions.push(graph_execution_context).map(Resource::::new_own) { + match self + .executions + .push(graph_execution_context) + .map(Resource::::new_own) + { Ok(res) => { return Ok(Ok(res)); } Err(_) => { - match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: "Can't create graph execution context".to_string()}){ + match self.errors.push(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: "Can't create graph execution context".to_string(), + }) { Ok(id) => { return Ok(Err(Resource::::new_own(id))); } @@ -131,14 +146,17 @@ pub mod ml { } } } - } } - Err(anyhow!("[graph::HostGraph] fn init_execution_context -> Not implemented")) + Err(anyhow!( + "[graph::HostGraph] fn init_execution_context -> Not implemented" + )) } fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { - self.graphs.remove(graph.rep()).context(format!("Can't find graph with ID = {}", graph.rep()))?; + self.graphs + .remove(graph.rep()) + .context(format!("Can't find graph with ID = {}", graph.rep()))?; Ok(()) } } @@ -149,20 +167,43 @@ pub mod ml { code: errors::ErrorCode, data: String, ) -> Result, anyhow::Error> { - self.errors.push(ErrorInternalData { code: code, message:data}).map(Resource::::new_own).map_err(|_|anyhow!("Can't allocate error")) + self.errors + .push(ErrorInternalData { + code: code, + message: data, + }) + .map(Resource::::new_own) + .map_err(|_| anyhow!("Can't allocate error")) } fn drop(&mut self, error: Resource) -> Result<(), anyhow::Error> { - self.errors.remove(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|_| ()) + self.errors + .remove(error.rep()) + .ok_or(anyhow!(format!( + "Can't find error with ID = {}", + error.rep() + ))) + .map(|_| ()) } fn code(&mut self, error: Resource) -> Result { - self.errors.get(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|e| e.code) + self.errors + .get(error.rep()) + .ok_or(anyhow!(format!( + "Can't find error with ID = {}", + error.rep() + ))) + .map(|e| e.code) } fn data(&mut self, error: Resource) -> Result { - self.errors.get(error.rep()).ok_or(anyhow!(format!("Can't find error with ID = {}", error.rep()))).map(|e| e.message.clone()) - + self.errors + .get(error.rep()) + .ok_or(anyhow!(format!( + "Can't find error with ID = {}", + error.rep() + ))) + .map(|e| e.message.clone()) } } impl tensor::HostTensor for MLHostImpl { @@ -177,25 +218,51 @@ pub mod ml { tensor_type: tensor_type, tensor_data: tensor_data, }; - self.tensors.push(tensor).map(Resource::::new_own).map_err(|_|anyhow!("Can't allocate tensor")) + self.tensors + .push(tensor) + .map(Resource::::new_own) + .map_err(|_| anyhow!("Can't allocate tensor")) } fn dimensions( &mut self, tensor: Resource, ) -> Result, anyhow::Error> { - self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_dimensions.clone()) + self.tensors + .get(tensor.rep()) + .ok_or(anyhow!(format!( + "Can't find tensor with ID = {}", + tensor.rep() + ))) + .map(|t| t.tensor_dimensions.clone()) } fn ty( &mut self, tensor: Resource, ) -> Result { - self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_type) + self.tensors + .get(tensor.rep()) + .ok_or(anyhow!(format!( + "Can't find tensor with ID = {}", + tensor.rep() + ))) + .map(|t| t.tensor_type) } - fn data(&mut self, tensor: Resource) -> Result { - self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep()))).map(|t| t.tensor_data.clone()) + fn data( + &mut self, + tensor: Resource, + ) -> Result { + self.tensors + .get(tensor.rep()) + .ok_or(anyhow!(format!( + "Can't find tensor with ID = {}", + tensor.rep() + ))) + .map(|t| t.tensor_data.clone()) } fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { - self.tensors.remove(tensor.rep()).context(format!("Can't find tensor with ID = {}", tensor.rep()))?; + self.tensors + .remove(tensor.rep()) + .context(format!("Can't find tensor with ID = {}", tensor.rep()))?; Ok(()) } } @@ -207,26 +274,40 @@ pub mod ml { input_name: String, tensor: Resource, ) -> Result>, anyhow::Error> { - let index = input_name.parse().context("Can't parse {} to usize for input_name")?; + let index = input_name + .parse() + .context("Can't parse {} to usize for input_name")?; // Construct the blob structure. TODO: there must be some good way to // discover the layout here; `desc` should not have to default to NHWC. - let tensor_resource = self.tensors.get(tensor.rep()).ok_or(anyhow!(format!("Can't find tensor with ID = {}", tensor.rep())))?; + let tensor_resource = self + .tensors + .get(tensor.rep()) + .context(format!("Can't find tensor with ID = {}", tensor.rep()))?; let precision = map_tensor_type_to_precision(tensor_resource.tensor_type); - let dimensions = tensor_resource.tensor_dimensions + let dimensions = tensor_resource + .tensor_dimensions .iter() .map(|&d| d as usize) .collect::>(); let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data)?; - let execution_context: &mut GraphExecutionContextInternalData = self.executions.get_mut(graph_execution_context.rep()).context(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep()))?; - let input_name = execution_context.cnn_network.get_input_name(index).context(format!("Can't find input with name = {}", index))?; + let execution_context: &mut GraphExecutionContextInternalData = self + .executions + .get_mut(graph_execution_context.rep()) + .context(format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + ))?; + let input_name = execution_context + .cnn_network + .get_input_name(index) + .context(format!("Can't find input with name = {}", index))?; let res = match execution_context.infer_request.set_blob(&input_name, &blob) { - Ok(res) => { - Ok(res) - } - Err(err) => { - Err(self.new(ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()))?) - } + Ok(res) => Ok(res), + Err(err) => Err(self.new( + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string()), + )?), }; Ok(res) } @@ -235,14 +316,19 @@ pub mod ml { &mut self, graph_execution_context: Resource, ) -> Result>, anyhow::Error> { - let graph_execution = self.executions + let graph_execution = self + .executions .get_mut(graph_execution_context.rep()) - .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; + .ok_or(anyhow!(format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + )))?; match graph_execution.infer_request.infer() { - Ok(..) => { Ok(Ok(())) } - Err(err) => { - Ok(Err(self.new(ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()))?)) - } + Ok(..) => Ok(Ok(())), + Err(err) => Ok(Err(self.new( + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string()), + )?)), } } @@ -252,41 +338,68 @@ pub mod ml { input_name: String, ) -> Result, Resource>, anyhow::Error> { - let index = input_name.parse::().context("Can't parse {} to usize for input_name")?; - let graph_execution = self.executions + let index = input_name + .parse::() + .context("Can't parse {} to usize for input_name")?; + let graph_execution = self + .executions .get_mut(graph_execution_context.rep()) - .ok_or(anyhow!(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; - let output_name = graph_execution.cnn_network.get_output_name(index).context("Can't find output name for ID = {index}")?; - let blob = graph_execution.infer_request.get_blob(&output_name).context("Can't get blob for output name = {output_name}")?; + .ok_or(anyhow!(format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + )))?; + let output_name = graph_execution + .cnn_network + .get_output_name(index) + .context("Can't find output name for ID = {index}")?; + let blob = graph_execution + .infer_request + .get_blob(&output_name) + .context("Can't get blob for output name = {output_name}")?; let tensor_desc = blob.tensor_desc().context("Can't get blob description")?; - let buffer = blob.buffer() - .context("Can't get blob buffer")?.iter() - .map(|&d| d as u8) - .collect::>(); - let tensor_dimensions = tensor_desc.dims().iter() - .map(|&d| d as u32) - .collect::>(); + let buffer = blob + .buffer() + .context("Can't get blob buffer")? + .iter() + .map(|&d| d as u8) + .collect::>(); + let tensor_dimensions = tensor_desc + .dims() + .iter() + .map(|&d| d as u32) + .collect::>(); let tensor = TensorInternalData { - tensor_dimensions:tensor_dimensions, + tensor_dimensions: tensor_dimensions, tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), tensor_data: buffer, }; - match self.tensors.push(tensor).map(Resource::::new_own) { + match self + .tensors + .push(tensor) + .map(Resource::::new_own) + { Ok(t) => { return Ok(Ok(t)); } Err(_) => { - return Ok(Err(self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))?)) + return Ok(Err(self.new( + ErrorCode::RuntimeError, + format!("Can't create tensor for get_output"), + )?)) } } } - fn drop(&mut self, execution: Resource) -> Result<(), anyhow::Error> { + fn drop( + &mut self, + execution: Resource, + ) -> Result<(), anyhow::Error> { let id = execution.rep(); - self.executions.remove(id).context("{Can't drow GraphExecutionContext with id = {id}")?; + self.executions + .remove(id) + .context("{Can't drow GraphExecutionContext with id = {id}")?; Ok(()) - } } @@ -298,26 +411,32 @@ pub mod ml { graph_encoding: GraphEncoding, target: ExecutionTarget, ) -> Result, Resource>, anyhow::Error> { - println!("[graph::Host] load entry point"); if graph.len() != 2 { - return Err(anyhow!("Expected 2 elements in graph builder vector")) + return Err(anyhow!("Expected 2 elements in graph builder vector")); } if graph_encoding != GraphEncoding::Openvino { - return Err(anyhow!("Only OpenVINO encoding is supported")) + return Err(anyhow!("Only OpenVINO encoding is supported")); } // Read the guest array. - let graph_internal_data = GraphInternalData { xml: graph[0].clone(), weights: graph[1].clone(), target: target }; + let graph_internal_data = GraphInternalData { + xml: graph[0].clone(), + weights: graph[1].clone(), + target: target, + }; match self.graphs.push(graph_internal_data) { Ok(graph_rep) => { return Ok(Ok(Resource::::new_own(graph_rep))); - }, + } Err(err) => { - match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err) }) { + match self.errors.push(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: format!("{:?}", err), + }) { Ok(error_rep) => { return Ok(Err(Resource::::new_own(error_rep))); } Err(err) => { - return Err(anyhow!("Can't create internal error for {:?}", err)); + return Err(anyhow!("Can't create internal error for {:?}", err)); } } } @@ -334,15 +453,15 @@ pub mod ml { impl inference::Host for MLHostImpl {} impl tensor::Host for MLHostImpl {} - - /// Return the execution target string expected by OpenVINO from the /// `ExecutionTarget` enum provided by wasi-nn. fn map_execution_target_to_string(target: ExecutionTarget) -> &'static str { match target { ExecutionTarget::Cpu => "CPU", ExecutionTarget::Gpu => "GPU", - ExecutionTarget::Tpu => unimplemented!("OpenVINO does not support TPU execution targets"), + ExecutionTarget::Tpu => { + unimplemented!("OpenVINO does not support TPU execution targets") + } } } From e6acbb1adb87cf1e408de17b6c70e3c5642f27eb Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Thu, 13 Jun 2024 13:35:11 +0300 Subject: [PATCH 17/26] Format code --- crates/core/tests/integration_test.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 81262e3605..b0973fc9a3 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -237,7 +237,6 @@ async fn test_host_component_imagenet_openvino_gpu() { assert_eq!(stdout, "0.96 -> mountain bike, all-terrain bike, off-roader\n0.01 -> bicycle-built-for-two, tandem bicycle, tandem\n0.00 -> alp"); } - #[tokio::test(flavor = "multi_thread")] #[cfg(not(tarpaulin))] async fn test_panic() { From e62fa9a51f893cbd036e0aa9d503d3d7b40dccf4 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Fri, 14 Jun 2024 17:19:18 +0300 Subject: [PATCH 18/26] automatically download mobilenet from github --- crates/core/Cargo.toml | 4 ++++ crates/core/build.rs | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 crates/core/build.rs diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index dcaf6b75b0..faf1af4157 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -34,3 +34,7 @@ tempfile = "3" tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } spin-componentize = { workspace = true } futures = "0.3" + +[build-dependencies] +curl = "^0.4" +anyhow = "^1" \ No newline at end of file diff --git a/crates/core/build.rs b/crates/core/build.rs new file mode 100644 index 0000000000..13d9565a73 --- /dev/null +++ b/crates/core/build.rs @@ -0,0 +1,49 @@ + +use std::env; +use std::fs; +use std::path::PathBuf; +extern crate curl; + + +use std::io::Write; +use curl::easy::Easy; +use anyhow; + +fn main() { + let base_url = "https://raw.githubusercontent.com/blocksense-network/imagenet_openvino/db44329b8e2b3398c9cc34dd56d94f3ce6fd6e21/";//images/0.jpg + + let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + let images_dir = imagenet_path.join("images"); + fs::create_dir_all(images_dir).unwrap(); + let files = [ + "model.xml", + "model.bin", + "images/0.jpg", + "images/1.jpg", + ]; + for file in files { + try_download(&(base_url.to_owned()+file), &imagenet_path.join(file)).unwrap(); + } + + println!("cargo:rerun-if-changed=build.rs"); +} + +fn try_download(url: &str, filename: &PathBuf) -> Result<(), anyhow::Error> { + let mut easy = Easy::new(); + easy.url(url).map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + + let mut dst = Vec::new(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|data| { + dst.extend_from_slice(data); + Ok(data.len()) + }).unwrap(); + transfer.perform().map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + } + { + let mut file = std::fs::File::create(filename)?; + file.write_all(dst.as_slice())?; + } + Ok(()) +} \ No newline at end of file From ba52d9100e4459fef7c33be3ace66d1d1f6e1230 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 17 Jun 2024 11:01:11 +0300 Subject: [PATCH 19/26] Stage cargo lock files --- Cargo.lock | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 685a803a8e..78ad3107ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1870,6 +1870,36 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2 0.5.6", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.52.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -4076,6 +4106,18 @@ dependencies = [ "threadpool", ] +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -7521,6 +7563,7 @@ dependencies = [ "cap-primitives 3.0.0", "cap-std 3.0.0", "crossbeam-channel", + "curl", "futures", "http 1.1.0", "io-extras", From 4764e77e9a5ad855b443202bdfbaf56d3bb69c70 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 17 Jun 2024 14:14:04 +0300 Subject: [PATCH 20/26] Remove unused anyhow --- crates/core/build.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/core/build.rs b/crates/core/build.rs index 13d9565a73..c8445cdb51 100644 --- a/crates/core/build.rs +++ b/crates/core/build.rs @@ -4,10 +4,8 @@ use std::fs; use std::path::PathBuf; extern crate curl; - use std::io::Write; use curl::easy::Easy; -use anyhow; fn main() { let base_url = "https://raw.githubusercontent.com/blocksense-network/imagenet_openvino/db44329b8e2b3398c9cc34dd56d94f3ce6fd6e21/";//images/0.jpg From 64ba50978d62ad8018c87c6e5e0c919d0b6f173e Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Thu, 20 Jun 2024 16:46:05 +0300 Subject: [PATCH 21/26] Test pass --- Cargo.lock | 46 +++ crates/core/Cargo.toml | 2 +- crates/core/build.rs | 2 +- crates/core/tests/core-wasi-test/Cargo.toml | 2 +- crates/core/tests/hello_component.rs | 4 +- crates/core/tests/ml_component.rs | 385 ++++++++++---------- 6 files changed, 247 insertions(+), 194 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78ad3107ac..eaf9ec1864 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1896,6 +1896,7 @@ dependencies = [ "libz-sys", "openssl-sys", "pkg-config", + "rustls-ffi", "vcpkg", "windows-sys 0.52.0", ] @@ -4937,6 +4938,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -6736,6 +6758,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-ffi" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +dependencies = [ + "libc", + "log", + "num_enum", + "rustls 0.20.9", + "rustls-pemfile 0.2.1", + "sct", + "webpki", +] + [[package]] name = "rustls-native-certs" version = "0.7.0" @@ -6749,6 +6786,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustls-pemfile" version = "0.3.0" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index faf1af4157..0dd1959373 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -36,5 +36,5 @@ spin-componentize = { workspace = true } futures = "0.3" [build-dependencies] -curl = "^0.4" +curl = { version = "^0.4", features = ["rustls"] } anyhow = "^1" \ No newline at end of file diff --git a/crates/core/build.rs b/crates/core/build.rs index c8445cdb51..9e136aa8cf 100644 --- a/crates/core/build.rs +++ b/crates/core/build.rs @@ -2,7 +2,7 @@ use std::env; use std::fs; use std::path::PathBuf; -extern crate curl; +//extern crate curl; use std::io::Write; use curl::easy::Easy; diff --git a/crates/core/tests/core-wasi-test/Cargo.toml b/crates/core/tests/core-wasi-test/Cargo.toml index e3fde5c310..6c590c7d56 100644 --- a/crates/core/tests/core-wasi-test/Cargo.toml +++ b/crates/core/tests/core-wasi-test/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" debug = true [dependencies] -wit-bindgen = "0.13.0" +wit-bindgen = "0.26.0" image2tensor = "0.3.1" [workspace] diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs index 4abca99b75..3f94c243a4 100644 --- a/crates/core/tests/hello_component.rs +++ b/crates/core/tests/hello_component.rs @@ -25,8 +25,8 @@ pub mod hello { pub struct HelloHostImpl {} impl gggg2::Host for HelloHostImpl { - fn say_hello(&mut self, x: String) -> wasmtime::Result { - Ok(format!("Hello bace {x}!")) + fn say_hello(&mut self, x: String) -> String { + format!("Hello bace {x}!") } } } diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 19456cac0f..bfb28f3a26 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -1,6 +1,11 @@ pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); + + use std::fmt::format; + + //use anyhow::Ok; + //use std::result::Result::Ok; use spin_core::HostComponent; use anyhow::{anyhow, Context}; @@ -14,6 +19,7 @@ pub mod ml { use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; use test::test::inference::GraphExecutionContext; use tokio::sync::Mutex; + use tokio::time::error::Elapsed; use wasmtime::component::Resource; use openvino::{Layout, Precision, TensorDesc}; @@ -71,28 +77,30 @@ pub mod ml { pub errors: table::Table, } - impl graph::HostGraph for MLHostImpl { - fn init_execution_context( - &mut self, - graph: Resource, - ) -> Result< - Result, Resource>, - anyhow::Error, - > { - // Construct the context if none is present; this is done lazily (i.e. - // upon actually loading a model) because it may fail to find and load - // the OpenVINO libraries. The laziness limits the extent of the error - // only to wasi-nn users, not all WASI users. - if self.openvino.is_none() { - self.openvino.replace(openvino::Core::new(None)?); - } - if self.openvino.is_some() { - if let Some(graph) = self.graphs.get(graph.rep()) { - let mut cnn_network = self - .openvino - .as_mut() - .expect("") - .read_network_from_buffer(&graph.xml, &graph.weights)?; + + + impl MLHostImpl { + + fn new_error(errors: &mut table::Table, code: ErrorCode, message: String) -> Resource { + errors + .push(ErrorInternalData { code: code, message: message,}) + .map(Resource::::new_own) + .expect("Can't allocate error") + } + + fn init_execution_context_internal( + graph: &GraphInternalData, + openvino: &mut Option, + executions: &mut table::Table, + ) -> Result, anyhow::Error> { + if openvino.is_none() { + openvino.replace(openvino::Core::new(None)?); + } + if openvino.is_some() { + let mut cnn_network = openvino + .as_mut() + .context("Can't create openvino graph without backend")? + .read_network_from_buffer(&graph.xml, &graph.weights)?; // Construct OpenVINO graph structures: `cnn_network` contains the graph // structure, `exec_network` can perform inference. @@ -110,47 +118,90 @@ pub mod ml { cnn_network.set_input_layout(&name, Layout::NHWC)?; } - let mut exec_network = self - .openvino + let mut exec_network = openvino .as_mut() .expect("") .load_network(&cnn_network, map_execution_target_to_string(graph.target))?; let infer_request = exec_network .create_infer_request() - .expect("Can't create InferRequest"); + .context("Can't create InferRequest")?; let graph_execution_context = GraphExecutionContextInternalData { cnn_network: cnn_network, executable_network: Mutex::new(exec_network), infer_request: infer_request, }; + return executions + .push(graph_execution_context) + .map(Resource::::new_own) + .map_err(|_|anyhow!("Can't store execution context")); + } + Err(anyhow!("Can't create openvino backend")) + } - match self - .executions - .push(graph_execution_context) - .map(Resource::::new_own) - { - Ok(res) => { - return Ok(Ok(res)); - } - Err(_) => { - match self.errors.push(ErrorInternalData { - code: ErrorCode::RuntimeError, - message: "Can't create graph execution context".to_string(), - }) { - Ok(id) => { - return Ok(Err(Resource::::new_own(id))); - } - Err(_) => { - return Err(anyhow!("Can't allocate error")); - } - } - } + fn get_output_internal(graph_execution: &mut GraphExecutionContextInternalData, input_name: String) -> Result { + let index = input_name + .parse::() + .map_err(|err| format!("Can't parse {} to usize for input_name", input_name))?; + let output_name = graph_execution + .cnn_network + .get_output_name(index) + .map_err(|err| format!("Can't find output name for ID = {index}"))?; + + let blob = graph_execution + .infer_request + .get_blob(&output_name) + .map_err(|err| format!("Can't get blob for output name = {output_name}"))?; + let tensor_desc = blob.tensor_desc() + .map_err(|err| format!("Can't get blob description"))?; + let buffer = blob + .buffer() + .map_err(|err| format!("Can't get blob buffer, error = {err}"))? + .iter() + .map(|&d| d as u8) + .collect::>(); + let tensor_dimensions = tensor_desc + .dims() + .iter() + .map(|&d| d as u32) + .collect::>(); + let tensor = TensorInternalData { + tensor_dimensions: tensor_dimensions, + tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), + tensor_data: buffer, + }; + Ok(tensor) + } + + } + + impl graph::HostGraph for MLHostImpl { + fn init_execution_context( + &mut self, + graph: Resource, + ) -> Result, Resource> { + let res = match self.graphs.get(graph.rep()) { + Some(graph) => { + MLHostImpl::init_execution_context_internal(graph, &mut self.openvino, &mut self.executions) + .map_err(|err| + ErrorInternalData { + code: ErrorCode::RuntimeError, + message: err.to_string(), } + ) + } + None => { + Err(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: "Can't create graph execution context".to_string(), + }) + } + }; + match res { + Ok(res) => { return Ok(res) } + Err(e) => { + return Err(MLHostImpl::new_error(&mut self.errors, e.code, e.message)); } } - Err(anyhow!( - "[graph::HostGraph] fn init_execution_context -> Not implemented" - )) } fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { @@ -165,45 +216,30 @@ pub mod ml { fn new( &mut self, code: errors::ErrorCode, - data: String, - ) -> Result, anyhow::Error> { - self.errors - .push(ErrorInternalData { - code: code, - message: data, - }) - .map(Resource::::new_own) - .map_err(|_| anyhow!("Can't allocate error")) + message: String, + ) -> Resource { + MLHostImpl::new_error(&mut self.errors, code, message) } - fn drop(&mut self, error: Resource) -> Result<(), anyhow::Error> { - self.errors - .remove(error.rep()) - .ok_or(anyhow!(format!( - "Can't find error with ID = {}", - error.rep() - ))) - .map(|_| ()) + fn drop(&mut self, error: Resource) -> std::result::Result<(), anyhow::Error> { + if let Some(e) = self.errors.remove(error.rep()) { + return Ok(()); + } + Err(anyhow!("Can't find error with ID = {}", error.rep())) } - fn code(&mut self, error: Resource) -> Result { - self.errors - .get(error.rep()) - .ok_or(anyhow!(format!( - "Can't find error with ID = {}", - error.rep() - ))) - .map(|e| e.code) + fn code(&mut self, error: Resource) -> ErrorCode { + if let Some(e) = self.errors.get(error.rep()) { + return e.code; + } + panic!("Can't find error with ID = {}", error.rep()); } - fn data(&mut self, error: Resource) -> Result { - self.errors - .get(error.rep()) - .ok_or(anyhow!(format!( - "Can't find error with ID = {}", - error.rep() - ))) - .map(|e| e.message.clone()) + fn data(&mut self, error: Resource) -> String { + if let Some(e) = self.errors.get(error.rep()) { + return e.message.clone(); + } + panic!("Can't find error with ID = {}", error.rep()); } } impl tensor::HostTensor for MLHostImpl { @@ -212,7 +248,7 @@ pub mod ml { tensor_dimensions: tensor::TensorDimensions, tensor_type: tensor::TensorType, tensor_data: tensor::TensorData, - ) -> Result, anyhow::Error> { + ) -> Resource { let tensor = TensorInternalData { tensor_dimensions: tensor_dimensions, tensor_type: tensor_type, @@ -221,49 +257,44 @@ pub mod ml { self.tensors .push(tensor) .map(Resource::::new_own) - .map_err(|_| anyhow!("Can't allocate tensor")) + .expect("Can't allocate tensor") } fn dimensions( &mut self, tensor: Resource, - ) -> Result, anyhow::Error> { - self.tensors - .get(tensor.rep()) - .ok_or(anyhow!(format!( - "Can't find tensor with ID = {}", - tensor.rep() - ))) - .map(|t| t.tensor_dimensions.clone()) + ) -> Vec { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_dimensions.clone(); + } + panic!("Can't find tensor with ID = {}", tensor.rep()); } + fn ty( &mut self, tensor: Resource, - ) -> Result { - self.tensors - .get(tensor.rep()) - .ok_or(anyhow!(format!( - "Can't find tensor with ID = {}", - tensor.rep() - ))) - .map(|t| t.tensor_type) + ) -> tensor::TensorType { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_type; + } + panic!("Can't find tensor with ID = {}", tensor.rep()); } + fn data( &mut self, tensor: Resource, - ) -> Result { - self.tensors - .get(tensor.rep()) - .ok_or(anyhow!(format!( - "Can't find tensor with ID = {}", - tensor.rep() - ))) - .map(|t| t.tensor_data.clone()) + ) -> tensor::TensorData { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_data.clone(); + } + panic!("Can't find tensor with ID = {}", tensor.rep()); } - fn drop(&mut self, tensor: Resource) -> Result<(), anyhow::Error> { + fn drop(&mut self, tensor: Resource) -> std::result::Result<(), anyhow::Error> { self.tensors .remove(tensor.rep()) - .context(format!("Can't find tensor with ID = {}", tensor.rep()))?; - Ok(()) + .context(format!("Can't find tensor with ID = {}", tensor.rep())) + .map(|_|()) + + } } @@ -273,16 +304,16 @@ pub mod ml { graph_execution_context: Resource, input_name: String, tensor: Resource, - ) -> Result>, anyhow::Error> { + ) -> Result<(), Resource> { let index = input_name .parse() - .context("Can't parse {} to usize for input_name")?; + .expect("Can't parse {} to usize for input_name"); // Construct the blob structure. TODO: there must be some good way to // discover the layout here; `desc` should not have to default to NHWC. let tensor_resource = self .tensors .get(tensor.rep()) - .context(format!("Can't find tensor with ID = {}", tensor.rep()))?; + .expect(format!("Can't find tensor with ID = {}", tensor.rep()).as_str()); let precision = map_tensor_type_to_precision(tensor_resource.tensor_type); let dimensions = tensor_resource .tensor_dimensions @@ -290,45 +321,46 @@ pub mod ml { .map(|&d| d as usize) .collect::>(); let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); - let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data)?; + let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data).expect("Error in Blob::new"); let execution_context: &mut GraphExecutionContextInternalData = self .executions .get_mut(graph_execution_context.rep()) - .context(format!( + .expect(format!( "Can't find graph execution context with ID = {}", graph_execution_context.rep() - ))?; + ).as_str()); let input_name = execution_context .cnn_network .get_input_name(index) - .context(format!("Can't find input with name = {}", index))?; - let res = match execution_context.infer_request.set_blob(&input_name, &blob) { + .expect(format!("Can't find input with name = {}", index).as_str()); + match execution_context.infer_request.set_blob(&input_name, &blob) { Ok(res) => Ok(res), - Err(err) => Err(self.new( + Err(err) => Err( + self.new( ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()), - )?), - }; - Ok(res) + )), + } + } fn compute( &mut self, graph_execution_context: Resource, - ) -> Result>, anyhow::Error> { + ) -> Result<(), Resource> { let graph_execution = self .executions .get_mut(graph_execution_context.rep()) - .ok_or(anyhow!(format!( - "Can't find graph execution context with ID = {}", - graph_execution_context.rep() - )))?; + .ok_or(MLHostImpl::new_error(&mut self.errors, + ErrorCode::RuntimeError, + format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; match graph_execution.infer_request.infer() { - Ok(..) => Ok(Ok(())), - Err(err) => Ok(Err(self.new( - ErrorCode::RuntimeError, - format!("Inference error = {:?}", err.to_string()), - )?)), + Ok(..) => Ok(()), + Err(err) => Err( + MLHostImpl::new_error(&mut self.errors, + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string())) + ), } } @@ -336,70 +368,41 @@ pub mod ml { &mut self, graph_execution_context: Resource, input_name: String, - ) -> Result, Resource>, anyhow::Error> + ) -> Result, Resource> { - let index = input_name - .parse::() - .context("Can't parse {} to usize for input_name")?; + let graph_execution = self .executions .get_mut(graph_execution_context.rep()) - .ok_or(anyhow!(format!( - "Can't find graph execution context with ID = {}", - graph_execution_context.rep() - )))?; - let output_name = graph_execution - .cnn_network - .get_output_name(index) - .context("Can't find output name for ID = {index}")?; - let blob = graph_execution - .infer_request - .get_blob(&output_name) - .context("Can't get blob for output name = {output_name}")?; - let tensor_desc = blob.tensor_desc().context("Can't get blob description")?; - let buffer = blob - .buffer() - .context("Can't get blob buffer")? - .iter() - .map(|&d| d as u8) - .collect::>(); - let tensor_dimensions = tensor_desc - .dims() - .iter() - .map(|&d| d as u32) - .collect::>(); + .ok_or(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())).unwrap(); - let tensor = TensorInternalData { - tensor_dimensions: tensor_dimensions, - tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), - tensor_data: buffer, - }; - match self - .tensors - .push(tensor) - .map(Resource::::new_own) - { - Ok(t) => { - return Ok(Ok(t)); + match MLHostImpl::get_output_internal(graph_execution, input_name) { + Ok(tensor) => { + self.tensors + .push(tensor) + .map(Resource::::new_own) + .map_err(|_| self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))) } - Err(_) => { - return Ok(Err(self.new( - ErrorCode::RuntimeError, - format!("Can't create tensor for get_output"), - )?)) + Err(err) => { + Err(MLHostImpl::new_error(&mut self.errors, + ErrorCode::RuntimeError, + err)) } } + } fn drop( &mut self, execution: Resource, - ) -> Result<(), anyhow::Error> { + ) -> std::result::Result<(), anyhow::Error> { let id = execution.rep(); self.executions .remove(id) - .context("{Can't drow GraphExecutionContext with id = {id}")?; - Ok(()) + .context("{Can't drow GraphExecutionContext with id = {id}") + .map(|_|()) + + } } @@ -410,12 +413,16 @@ pub mod ml { graph: Vec, graph_encoding: GraphEncoding, target: ExecutionTarget, - ) -> Result, Resource>, anyhow::Error> { + ) -> Result, Resource> { if graph.len() != 2 { - return Err(anyhow!("Expected 2 elements in graph builder vector")); + return Err(MLHostImpl::new_error(&mut self.errors, + ErrorCode::RuntimeError, + format!("Expected 2 elements in graph builder vector"))); } if graph_encoding != GraphEncoding::Openvino { - return Err(anyhow!("Only OpenVINO encoding is supported")); + return Err(MLHostImpl::new_error(&mut self.errors, + ErrorCode::RuntimeError, + format!("Only OpenVINO encoding is supported"))); } // Read the guest array. let graph_internal_data = GraphInternalData { @@ -425,7 +432,7 @@ pub mod ml { }; match self.graphs.push(graph_internal_data) { Ok(graph_rep) => { - return Ok(Ok(Resource::::new_own(graph_rep))); + return Ok(Resource::::new_own(graph_rep)); } Err(err) => { match self.errors.push(ErrorInternalData { @@ -433,10 +440,10 @@ pub mod ml { message: format!("{:?}", err), }) { Ok(error_rep) => { - return Ok(Err(Resource::::new_own(error_rep))); + return Err(Resource::::new_own(error_rep)); } Err(err) => { - return Err(anyhow!("Can't create internal error for {:?}", err)); + panic!("Can't create internal error for {:?}", err); } } } @@ -445,8 +452,8 @@ pub mod ml { fn load_by_name( &mut self, _graph: String, - ) -> Result, Resource>, anyhow::Error> { - Err(anyhow!("[graph::Host] fn load_by_name -> Not implemented")) + ) -> Result, Resource> { + panic!("[graph::Host] fn load_by_name -> Not implemented"); } } From ef66a8158715f0549ac735f44018c355656a718f Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Thu, 20 Jun 2024 17:42:02 +0300 Subject: [PATCH 22/26] Format code --- crates/core/build.rs | 38 +-- .../core/tests/core-wasi-test/src/imagenet.rs | 115 +++++-- .../core-wasi-test/src/imagenet_classes.rs | 2 - crates/core/tests/core-wasi-test/src/main.rs | 10 +- crates/core/tests/ml_component.rs | 281 +++++++++--------- 5 files changed, 251 insertions(+), 195 deletions(-) diff --git a/crates/core/build.rs b/crates/core/build.rs index 9e136aa8cf..9303042027 100644 --- a/crates/core/build.rs +++ b/crates/core/build.rs @@ -1,47 +1,47 @@ - use std::env; use std::fs; use std::path::PathBuf; //extern crate curl; -use std::io::Write; use curl::easy::Easy; +use std::io::Write; fn main() { - let base_url = "https://raw.githubusercontent.com/blocksense-network/imagenet_openvino/db44329b8e2b3398c9cc34dd56d94f3ce6fd6e21/";//images/0.jpg + let base_url = "https://raw.githubusercontent.com/blocksense-network/imagenet_openvino/db44329b8e2b3398c9cc34dd56d94f3ce6fd6e21/"; //images/0.jpg - let imagenet_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); let images_dir = imagenet_path.join("images"); fs::create_dir_all(images_dir).unwrap(); - let files = [ - "model.xml", - "model.bin", - "images/0.jpg", - "images/1.jpg", - ]; + let files = ["model.xml", "model.bin", "images/0.jpg", "images/1.jpg"]; for file in files { - try_download(&(base_url.to_owned()+file), &imagenet_path.join(file)).unwrap(); + try_download(&(base_url.to_owned() + file), &imagenet_path.join(file)).unwrap(); } - + println!("cargo:rerun-if-changed=build.rs"); } fn try_download(url: &str, filename: &PathBuf) -> Result<(), anyhow::Error> { let mut easy = Easy::new(); - easy.url(url).map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + easy.url(url) + .map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; let mut dst = Vec::new(); { let mut transfer = easy.transfer(); - transfer.write_function(|data| { - dst.extend_from_slice(data); - Ok(data.len()) - }).unwrap(); - transfer.perform().map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + transfer + .write_function(|data| { + dst.extend_from_slice(data); + Ok(data.len()) + }) + .unwrap(); + transfer + .perform() + .map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; } { let mut file = std::fs::File::create(filename)?; file.write_all(dst.as_slice())?; } Ok(()) -} \ No newline at end of file +} diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs index 6d535451bb..0a200564ab 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -1,11 +1,9 @@ - - -use crate::ml::test::test::{graph, tensor, inference}; +use crate::ml::test::test::{graph, inference, tensor}; use image2tensor; use image2tensor::convert_image_to_tensor_bytes; -use crate::Path; use crate::imagenet_classes; +use crate::Path; pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { if elapsed < 1000 { @@ -13,7 +11,11 @@ pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { } else if elapsed < 1000 * 1000 { format!("`{}` took {:.2} µs", fn_name, elapsed as f64 / 1000.0) } else { - format!("`{}` took {:.2} ms", fn_name, elapsed as f64 / 1000.0 / 1000.0) + format!( + "`{}` took {:.2} ms", + fn_name, + elapsed as f64 / 1000.0 / 1000.0 + ) } } @@ -33,49 +35,67 @@ fn map_string_to_execution_target(target: &str) -> Result Ok(graph::ExecutionTarget::Cpu), "GPU" => Ok(graph::ExecutionTarget::Gpu), "TPU" => Ok(graph::ExecutionTarget::Tpu), - _ => { - Err(format!("Unknown execution targer = {}", target)) - } + _ => Err(format!("Unknown execution targer = {}", target)), } } - -pub fn imagenet_openvino_test(path_as_string: String, target_as_string: String, image_file: String) -> std::result::Result<(), Box> { +pub fn imagenet_openvino_test( + path_as_string: String, + target_as_string: String, + image_file: String, +) -> std::result::Result<(), Box> { let path = Path::new(&path_as_string); let target = map_string_to_execution_target(&target_as_string)?; let model = { let start_for_elapsed_macro = std::time::Instant::now(); let model: Vec = std::fs::read(&path.join("model.xml"))?; let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Loaded model from xml {} {}", bytes_to_string(model.len()), elapsed_to_string("fs::read", elapsed)); + eprintln!( + "Loaded model from xml {} {}", + bytes_to_string(model.len()), + elapsed_to_string("fs::read", elapsed) + ); model }; let weights = { let start_for_elapsed_macro = std::time::Instant::now(); let weights = std::fs::read(&path.join("model.bin"))?; let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Loaded weigths {} {}", bytes_to_string(weights.len()), elapsed_to_string("fs::read", elapsed)); + eprintln!( + "Loaded weigths {} {}", + bytes_to_string(weights.len()), + elapsed_to_string("fs::read", elapsed) + ); weights }; let imagenet_graph = { let start_for_elapsed_macro = std::time::Instant::now(); - let imagenet_graph = graph::load(&[model, weights], graph::GraphEncoding::Openvino, target).unwrap(); + let imagenet_graph = + graph::load(&[model, weights], graph::GraphEncoding::Openvino, target).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); eprintln!("---- {:?} ----", target); - eprintln!("Loaded graph with ID: {:?} {}", imagenet_graph, elapsed_to_string("graph::load", elapsed)); + eprintln!( + "Loaded graph with ID: {:?} {}", + imagenet_graph, + elapsed_to_string("graph::load", elapsed) + ); imagenet_graph }; let context = { let start_for_elapsed_macro = std::time::Instant::now(); let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Created context with ID: {:?} {}", context, elapsed_to_string("Graph::init_execution_context", elapsed)); + eprintln!( + "Created context with ID: {:?} {}", + context, + elapsed_to_string("Graph::init_execution_context", elapsed) + ); context }; - let tensor_dimensions:Vec = vec![1, 3, 224, 224]; + let tensor_dimensions: Vec = vec![1, 3, 224, 224]; let tensor_data = convert_image_to_tensor_bytes( - &image_file,//"images/0.jpg", + &image_file, //"images/0.jpg", tensor_dimensions[2], tensor_dimensions[3], image2tensor::TensorType::F32, @@ -83,47 +103,70 @@ pub fn imagenet_openvino_test(path_as_string: String, target_as_string: String, ) .or_else(|e| Err(e)) .unwrap(); - - + let tensor_id = { let start_for_elapsed_macro = std::time::Instant::now(); let tensor_type = tensor::TensorType::Fp32; let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Created tensor with ID: {:?} {}", tensor_id, elapsed_to_string("Tensor::new", elapsed)); + eprintln!( + "Created tensor with ID: {:?} {}", + tensor_id, + elapsed_to_string("Tensor::new", elapsed) + ); tensor_id }; let input_name = "0"; { let start_for_elapsed_macro = std::time::Instant::now(); - let set_input_result = inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); + let set_input_result = + inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Input set with ID: {:?} {}", set_input_result, elapsed_to_string("GraphExecutionContext::set_input", elapsed)); + eprintln!( + "Input set with ID: {:?} {}", + set_input_result, + elapsed_to_string("GraphExecutionContext::set_input", elapsed) + ); } { let start_for_elapsed_macro = std::time::Instant::now(); let _infered_result = inference::GraphExecutionContext::compute(&context).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Executed graph inference. {}", elapsed_to_string("GraphExecutionContext::compute", elapsed)); + eprintln!( + "Executed graph inference. {}", + elapsed_to_string("GraphExecutionContext::compute", elapsed) + ); } - let output_result_id = { + let output_result_id = { let start_for_elapsed_macro = std::time::Instant::now(); - let output_result_id = inference::GraphExecutionContext::get_output(&context, input_name).unwrap(); + let output_result_id = + inference::GraphExecutionContext::get_output(&context, input_name).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Obtaining output {}", elapsed_to_string("GraphExecutionContext::get_output", elapsed)); + eprintln!( + "Obtaining output {}", + elapsed_to_string("GraphExecutionContext::get_output", elapsed) + ); output_result_id }; - let (output_data, output_dimensions, output_type) = { + let (output_data, output_dimensions, output_type) = { let start_for_elapsed_macro = std::time::Instant::now(); let output_data = tensor::Tensor::data(&output_result_id); let output_dimensions = tensor::Tensor::dimensions(&output_result_id); let output_type = tensor::Tensor::ty(&output_result_id); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); - eprintln!("Copying data from tensor. {}", elapsed_to_string("Tensor::data+dimensions+type", elapsed)); + eprintln!( + "Copying data from tensor. {}", + elapsed_to_string("Tensor::data+dimensions+type", elapsed) + ); (output_data, output_dimensions, output_type) }; - if output_dimensions.len() == 2 && output_dimensions[0] == 1 && output_dimensions[1] == 1001 && output_type == tensor::TensorType::Fp32 { - let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; + if output_dimensions.len() == 2 + && output_dimensions[0] == 1 + && output_dimensions[1] == 1001 + && output_type == tensor::TensorType::Fp32 + { + let output_vec_f32 = + unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; let results = sort_results(&output_vec_f32); for i in 0..3 { println!( @@ -133,7 +176,10 @@ pub fn imagenet_openvino_test(path_as_string: String, target_as_string: String, ); } } else { - eprintln!("Output not as expected, output = {:?} {:?}", &output_dimensions, &output_type); + eprintln!( + "Output not as expected, output = {:?} {:?}", + &output_dimensions, &output_type + ); } Ok(()) } @@ -146,7 +192,10 @@ pub fn sort_results(buffer: &[f32]) -> Vec { .iter() .skip(1) .enumerate() - .map(|(c, p)| InferenceResult{ index: c, weight: *p}) + .map(|(c, p)| InferenceResult { + index: c, + weight: *p, + }) .collect(); results.sort_by(|a, b| b.weight.partial_cmp(&a.weight).unwrap()); results @@ -154,7 +203,7 @@ pub fn sort_results(buffer: &[f32]) -> Vec { // A wrapper for class ID and match probabilities. #[derive(Debug, PartialEq)] -pub struct InferenceResult{ +pub struct InferenceResult { pub index: usize, pub weight: f32, } diff --git a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs index 2ab1ed9bcb..6aab4910c3 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs @@ -1019,5 +1019,3 @@ pub const IMAGENET_CLASSES: [&str; 1000] = [ "ear, spike, capitulum", "toilet tissue, toilet paper, bathroom tissue" ]; - - diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 93cbb7010e..80a5ed73ae 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -24,22 +24,17 @@ mod ml { }); } -mod imagenet_classes; mod imagenet; +mod imagenet_classes; use imagenet::imagenet_openvino_test; - - use std::path::Path; use crate::hello::test::test::gggg2::say_hello; - - type Result = std::result::Result<(), Box>; - fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); @@ -89,7 +84,6 @@ fn main() -> Result { let target_as_string = args.next().expect("target"); let image_file_as_string = args.next().expect("image_file"); _ = imagenet_openvino_test(path_as_string, target_as_string, image_file_as_string); - } "sleep" => { let duration = @@ -104,4 +98,4 @@ fn main() -> Result { cmd => panic!("unknown cmd {cmd}"), }; Ok(()) -} \ No newline at end of file +} diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index bfb28f3a26..2fd5578c6e 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -1,7 +1,6 @@ pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); - use std::fmt::format; //use anyhow::Ok; @@ -53,7 +52,7 @@ pub mod ml { pub struct GraphExecutionContextInternalData { pub cnn_network: openvino::CNNNetwork, - pub executable_network: Mutex, + //pub executable_network: Mutex, pub infer_request: openvino::InferRequest, } @@ -77,15 +76,19 @@ pub mod ml { pub errors: table::Table, } - - impl MLHostImpl { - - fn new_error(errors: &mut table::Table, code: ErrorCode, message: String) -> Resource { + fn new_error( + errors: &mut table::Table, + code: ErrorCode, + message: String, + ) -> Resource { errors - .push(ErrorInternalData { code: code, message: message,}) - .map(Resource::::new_own) - .expect("Can't allocate error") + .push(ErrorInternalData { + code: code, + message: message, + }) + .map(Resource::::new_own) + .expect("Can't allocate error") } fn init_execution_context_internal( @@ -95,64 +98,73 @@ pub mod ml { ) -> Result, anyhow::Error> { if openvino.is_none() { openvino.replace(openvino::Core::new(None)?); - } + } if openvino.is_some() { let mut cnn_network = openvino .as_mut() .context("Can't create openvino graph without backend")? .read_network_from_buffer(&graph.xml, &graph.weights)?; - // Construct OpenVINO graph structures: `cnn_network` contains the graph - // structure, `exec_network` can perform inference. - //let core = self - // .0 - // .as_mut() - // .expect("openvino::Core was previously constructed"); - //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; - - // TODO: this is a temporary workaround. We need a more elegant way to - // specify the layout in the long run. However, without this newer - // versions of OpenVINO will fail due to parameter mismatch. - for i in 0..cnn_network.get_inputs_len().unwrap() { - let name = cnn_network.get_input_name(i)?; - cnn_network.set_input_layout(&name, Layout::NHWC)?; - } + // Construct OpenVINO graph structures: `cnn_network` contains the graph + // structure, `exec_network` can perform inference. + //let core = self + // .0 + // .as_mut() + // .expect("openvino::Core was previously constructed"); + //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; + + // TODO: this is a temporary workaround. We need a more elegant way to + // specify the layout in the long run. However, without this newer + // versions of OpenVINO will fail due to parameter mismatch. + for i in 0..cnn_network.get_inputs_len().unwrap() { + let name = cnn_network.get_input_name(i)?; + cnn_network.set_input_layout(&name, Layout::NHWC)?; + } - let mut exec_network = openvino - .as_mut() - .expect("") - .load_network(&cnn_network, map_execution_target_to_string(graph.target))?; - let infer_request = exec_network - .create_infer_request() - .context("Can't create InferRequest")?; - let graph_execution_context = GraphExecutionContextInternalData { - cnn_network: cnn_network, - executable_network: Mutex::new(exec_network), - infer_request: infer_request, - }; - return executions - .push(graph_execution_context) - .map(Resource::::new_own) - .map_err(|_|anyhow!("Can't store execution context")); + let mut exec_network = openvino + .as_mut() + .expect("") + .load_network(&cnn_network, map_execution_target_to_string(graph.target))?; + let infer_request = exec_network + .create_infer_request() + .context("Can't create InferRequest")?; + let graph_execution_context = GraphExecutionContextInternalData { + cnn_network: cnn_network, + //executable_network: Mutex::new(exec_network), + infer_request: infer_request, + }; + return executions + .push(graph_execution_context) + .map(Resource::::new_own) + .map_err(|_| anyhow!("Can't store execution context")); } Err(anyhow!("Can't create openvino backend")) } - fn get_output_internal(graph_execution: &mut GraphExecutionContextInternalData, input_name: String) -> Result { - let index = input_name - .parse::() - .map_err(|err| format!("Can't parse {} to usize for input_name", input_name))?; + fn get_output_internal( + graph_execution: &mut GraphExecutionContextInternalData, + input_name: String, + ) -> Result { + let index = input_name.parse::().map_err(|err| { + format!( + "Can't parse {} to usize for input_name, err = {err}", + input_name + ) + })?; let output_name = graph_execution .cnn_network .get_output_name(index) - .map_err(|err| format!("Can't find output name for ID = {index}"))?; + .map_err(|err| format!("Can't find output name for ID = {index}, err = {err}"))?; let blob = graph_execution .infer_request .get_blob(&output_name) - .map_err(|err| format!("Can't get blob for output name = {output_name}"))?; - let tensor_desc = blob.tensor_desc() - .map_err(|err| format!("Can't get blob description"))?; + .map_err(|err| { + format!("Can't get blob for output name = {output_name}, err = {err}") + })?; + let tensor_desc = blob + .tensor_desc() + .map_err(|err| format!("Can't get blob description, err = {err}"))?; let buffer = blob .buffer() .map_err(|err| format!("Can't get blob buffer, error = {err}"))? @@ -171,7 +183,6 @@ pub mod ml { }; Ok(tensor) } - } impl graph::HostGraph for MLHostImpl { @@ -180,24 +191,22 @@ pub mod ml { graph: Resource, ) -> Result, Resource> { let res = match self.graphs.get(graph.rep()) { - Some(graph) => { - MLHostImpl::init_execution_context_internal(graph, &mut self.openvino, &mut self.executions) - .map_err(|err| - ErrorInternalData { - code: ErrorCode::RuntimeError, - message: err.to_string(), - } + Some(graph) => MLHostImpl::init_execution_context_internal( + graph, + &mut self.openvino, + &mut self.executions, ) - } - None => { - Err(ErrorInternalData { - code: ErrorCode::RuntimeError, - message: "Can't create graph execution context".to_string(), - }) - } + .map_err(|err| ErrorInternalData { + code: ErrorCode::RuntimeError, + message: err.to_string(), + }), + None => Err(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: "Can't create graph execution context".to_string(), + }), }; match res { - Ok(res) => { return Ok(res) } + Ok(res) => return Ok(res), Err(e) => { return Err(MLHostImpl::new_error(&mut self.errors, e.code, e.message)); } @@ -207,25 +216,24 @@ pub mod ml { fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { self.graphs .remove(graph.rep()) - .context(format!("Can't find graph with ID = {}", graph.rep()))?; - Ok(()) + .context(format!("Can't find graph with ID = {}", graph.rep())) + .map(|_| ()) } } impl errors::HostError for MLHostImpl { - fn new( - &mut self, - code: errors::ErrorCode, - message: String, - ) -> Resource { + fn new(&mut self, code: errors::ErrorCode, message: String) -> Resource { MLHostImpl::new_error(&mut self.errors, code, message) } - fn drop(&mut self, error: Resource) -> std::result::Result<(), anyhow::Error> { - if let Some(e) = self.errors.remove(error.rep()) { - return Ok(()); - } - Err(anyhow!("Can't find error with ID = {}", error.rep())) + fn drop( + &mut self, + error: Resource, + ) -> std::result::Result<(), anyhow::Error> { + self.errors + .remove(error.rep()) + .context(format!("Can't find error with ID = {}", error.rep())) + .map(|_| ()) } fn code(&mut self, error: Resource) -> ErrorCode { @@ -259,42 +267,34 @@ pub mod ml { .map(Resource::::new_own) .expect("Can't allocate tensor") } - fn dimensions( - &mut self, - tensor: Resource, - ) -> Vec { + fn dimensions(&mut self, tensor: Resource) -> Vec { if let Some(t) = self.tensors.get(tensor.rep()) { return t.tensor_dimensions.clone(); } panic!("Can't find tensor with ID = {}", tensor.rep()); } - fn ty( - &mut self, - tensor: Resource, - ) -> tensor::TensorType { + fn ty(&mut self, tensor: Resource) -> tensor::TensorType { if let Some(t) = self.tensors.get(tensor.rep()) { return t.tensor_type; } panic!("Can't find tensor with ID = {}", tensor.rep()); } - fn data( - &mut self, - tensor: Resource, - ) -> tensor::TensorData { + fn data(&mut self, tensor: Resource) -> tensor::TensorData { if let Some(t) = self.tensors.get(tensor.rep()) { return t.tensor_data.clone(); } panic!("Can't find tensor with ID = {}", tensor.rep()); } - fn drop(&mut self, tensor: Resource) -> std::result::Result<(), anyhow::Error> { + fn drop( + &mut self, + tensor: Resource, + ) -> std::result::Result<(), anyhow::Error> { self.tensors .remove(tensor.rep()) .context(format!("Can't find tensor with ID = {}", tensor.rep())) - .map(|_|()) - - + .map(|_| ()) } } @@ -321,27 +321,29 @@ pub mod ml { .map(|&d| d as usize) .collect::>(); let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); - let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data).expect("Error in Blob::new"); + let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data) + .expect("Error in Blob::new"); let execution_context: &mut GraphExecutionContextInternalData = self .executions .get_mut(graph_execution_context.rep()) - .expect(format!( - "Can't find graph execution context with ID = {}", - graph_execution_context.rep() - ).as_str()); + .expect( + format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + ) + .as_str(), + ); let input_name = execution_context .cnn_network .get_input_name(index) .expect(format!("Can't find input with name = {}", index).as_str()); match execution_context.infer_request.set_blob(&input_name, &blob) { Ok(res) => Ok(res), - Err(err) => Err( - self.new( + Err(err) => Err(self.new( ErrorCode::RuntimeError, format!("Inference error = {:?}", err.to_string()), )), } - } fn compute( @@ -351,16 +353,21 @@ pub mod ml { let graph_execution = self .executions .get_mut(graph_execution_context.rep()) - .ok_or(MLHostImpl::new_error(&mut self.errors, - ErrorCode::RuntimeError, - format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())))?; + .ok_or(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + ), + ))?; match graph_execution.infer_request.infer() { Ok(..) => Ok(()), - Err(err) => Err( - MLHostImpl::new_error(&mut self.errors, - ErrorCode::RuntimeError, - format!("Inference error = {:?}", err.to_string())) - ), + Err(err) => Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string()), + )), } } @@ -368,28 +375,34 @@ pub mod ml { &mut self, graph_execution_context: Resource, input_name: String, - ) -> Result, Resource> - { - + ) -> Result, Resource> { let graph_execution = self .executions .get_mut(graph_execution_context.rep()) - .ok_or(format!("Can't find graph execution context with ID = {}", graph_execution_context.rep())).unwrap(); + .ok_or(format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + )) + .unwrap(); match MLHostImpl::get_output_internal(graph_execution, input_name) { - Ok(tensor) => { - self.tensors + Ok(tensor) => self + .tensors .push(tensor) .map(Resource::::new_own) - .map_err(|_| self.new(ErrorCode::RuntimeError, format!("Can't create tensor for get_output"))) - } - Err(err) => { - Err(MLHostImpl::new_error(&mut self.errors, - ErrorCode::RuntimeError, - err)) - } + .map_err(|_| { + MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!("Can't create tensor for get_output"), + ) + }), + Err(err) => Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + err, + )), } - } fn drop( @@ -400,9 +413,7 @@ pub mod ml { self.executions .remove(id) .context("{Can't drow GraphExecutionContext with id = {id}") - .map(|_|()) - - + .map(|_| ()) } } @@ -415,14 +426,18 @@ pub mod ml { target: ExecutionTarget, ) -> Result, Resource> { if graph.len() != 2 { - return Err(MLHostImpl::new_error(&mut self.errors, - ErrorCode::RuntimeError, - format!("Expected 2 elements in graph builder vector"))); + return Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!("Expected 2 elements in graph builder vector"), + )); } if graph_encoding != GraphEncoding::Openvino { - return Err(MLHostImpl::new_error(&mut self.errors, - ErrorCode::RuntimeError, - format!("Only OpenVINO encoding is supported"))); + return Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!("Only OpenVINO encoding is supported"), + )); } // Read the guest array. let graph_internal_data = GraphInternalData { @@ -453,7 +468,7 @@ pub mod ml { &mut self, _graph: String, ) -> Result, Resource> { - panic!("[graph::Host] fn load_by_name -> Not implemented"); + panic!("[graph::Host] fn load_by_name -> Not implemented"); } } From a1b746156b933d11e8cd902ffef03ec64c2c94c5 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Tue, 25 Jun 2024 17:41:21 +0300 Subject: [PATCH 23/26] Fix lint errors --- .../core/tests/core-wasi-test/src/imagenet.rs | 22 +- crates/core/tests/ml_component.rs | 62 ++--- crates/doctor/src/test.rs | 2 +- crates/locked-app/src/locked.rs | 2 - examples/spin-timer/Cargo.lock | 225 +++++++++++++++++- 5 files changed, 243 insertions(+), 70 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs index 0a200564ab..4ec5f14476 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -1,5 +1,4 @@ use crate::ml::test::test::{graph, inference, tensor}; -use image2tensor; use image2tensor::convert_image_to_tensor_bytes; use crate::imagenet_classes; @@ -48,7 +47,7 @@ pub fn imagenet_openvino_test( let target = map_string_to_execution_target(&target_as_string)?; let model = { let start_for_elapsed_macro = std::time::Instant::now(); - let model: Vec = std::fs::read(&path.join("model.xml"))?; + let model: Vec = std::fs::read(path.join("model.xml"))?; let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); eprintln!( "Loaded model from xml {} {}", @@ -59,7 +58,7 @@ pub fn imagenet_openvino_test( }; let weights = { let start_for_elapsed_macro = std::time::Instant::now(); - let weights = std::fs::read(&path.join("model.bin"))?; + let weights = std::fs::read(path.join("model.bin"))?; let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); eprintln!( "Loaded weigths {} {}", @@ -101,7 +100,6 @@ pub fn imagenet_openvino_test( image2tensor::TensorType::F32, image2tensor::ColorOrder::BGR, ) - .or_else(|e| Err(e)) .unwrap(); let tensor_id = { @@ -119,18 +117,16 @@ pub fn imagenet_openvino_test( let input_name = "0"; { let start_for_elapsed_macro = std::time::Instant::now(); - let set_input_result = - inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); + inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); eprintln!( - "Input set with ID: {:?} {}", - set_input_result, + "Input set {}", elapsed_to_string("GraphExecutionContext::set_input", elapsed) ); } { let start_for_elapsed_macro = std::time::Instant::now(); - let _infered_result = inference::GraphExecutionContext::compute(&context).unwrap(); + inference::GraphExecutionContext::compute(&context).unwrap(); let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); eprintln!( "Executed graph inference. {}", @@ -167,12 +163,12 @@ pub fn imagenet_openvino_test( { let output_vec_f32 = unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; - let results = sort_results(&output_vec_f32); - for i in 0..3 { + let results = sort_results(output_vec_f32); + for res in results.iter().take(3) { println!( "{:.2} -> {}", - results[i].weight, - imagenet_classes::IMAGENET_CLASSES[results[i].index], + res.weight, + imagenet_classes::IMAGENET_CLASSES[res.index], ); } } else { diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/ml_component.rs index 2fd5578c6e..309f56d2f3 100644 --- a/crates/core/tests/ml_component.rs +++ b/crates/core/tests/ml_component.rs @@ -1,10 +1,6 @@ pub mod ml { wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); - use std::fmt::format; - - //use anyhow::Ok; - //use std::result::Result::Ok; use spin_core::HostComponent; use anyhow::{anyhow, Context}; @@ -17,12 +13,9 @@ pub mod ml { use test::test::errors::ErrorCode; use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; use test::test::inference::GraphExecutionContext; - use tokio::sync::Mutex; - use tokio::time::error::Elapsed; use wasmtime::component::Resource; use openvino::{Layout, Precision, TensorDesc}; - use table; #[derive(Clone)] pub struct MLHostComponent; @@ -83,10 +76,7 @@ pub mod ml { message: String, ) -> Resource { errors - .push(ErrorInternalData { - code: code, - message: message, - }) + .push(ErrorInternalData { code, message }) .map(Resource::::new_own) .expect("Can't allocate error") } @@ -129,9 +119,9 @@ pub mod ml { .create_infer_request() .context("Can't create InferRequest")?; let graph_execution_context = GraphExecutionContextInternalData { - cnn_network: cnn_network, + cnn_network, //executable_network: Mutex::new(exec_network), - infer_request: infer_request, + infer_request, }; return executions .push(graph_execution_context) @@ -168,16 +158,14 @@ pub mod ml { let buffer = blob .buffer() .map_err(|err| format!("Can't get blob buffer, error = {err}"))? - .iter() - .map(|&d| d as u8) - .collect::>(); + .to_vec(); let tensor_dimensions = tensor_desc .dims() .iter() .map(|&d| d as u32) .collect::>(); let tensor = TensorInternalData { - tensor_dimensions: tensor_dimensions, + tensor_dimensions, tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), tensor_data: buffer, }; @@ -206,10 +194,8 @@ pub mod ml { }), }; match res { - Ok(res) => return Ok(res), - Err(e) => { - return Err(MLHostImpl::new_error(&mut self.errors, e.code, e.message)); - } + Ok(res) => Ok(res), + Err(e) => Err(MLHostImpl::new_error(&mut self.errors, e.code, e.message)), } } @@ -258,9 +244,9 @@ pub mod ml { tensor_data: tensor::TensorData, ) -> Resource { let tensor = TensorInternalData { - tensor_dimensions: tensor_dimensions, - tensor_type: tensor_type, - tensor_data: tensor_data, + tensor_dimensions, + tensor_type, + tensor_data, }; self.tensors .push(tensor) @@ -313,7 +299,7 @@ pub mod ml { let tensor_resource = self .tensors .get(tensor.rep()) - .expect(format!("Can't find tensor with ID = {}", tensor.rep()).as_str()); + .unwrap_or_else(|| panic!("Can't find tensor with ID = {}", tensor.rep())); let precision = map_tensor_type_to_precision(tensor_resource.tensor_type); let dimensions = tensor_resource .tensor_dimensions @@ -326,17 +312,11 @@ pub mod ml { let execution_context: &mut GraphExecutionContextInternalData = self .executions .get_mut(graph_execution_context.rep()) - .expect( - format!( - "Can't find graph execution context with ID = {}", - graph_execution_context.rep() - ) - .as_str(), - ); + .unwrap_or_else(|| panic!("Can't find tensor with ID = {}", tensor.rep())); let input_name = execution_context .cnn_network .get_input_name(index) - .expect(format!("Can't find input with name = {}", index).as_str()); + .unwrap_or_else(|_| panic!("Can't find input with name = {}", index)); match execution_context.infer_request.set_blob(&input_name, &blob) { Ok(res) => Ok(res), Err(err) => Err(self.new( @@ -394,7 +374,7 @@ pub mod ml { MLHostImpl::new_error( &mut self.errors, ErrorCode::RuntimeError, - format!("Can't create tensor for get_output"), + "Can't create tensor for get_output".to_string(), ) }), Err(err) => Err(MLHostImpl::new_error( @@ -429,34 +409,30 @@ pub mod ml { return Err(MLHostImpl::new_error( &mut self.errors, ErrorCode::RuntimeError, - format!("Expected 2 elements in graph builder vector"), + "Expected 2 elements in graph builder vector".to_string(), )); } if graph_encoding != GraphEncoding::Openvino { return Err(MLHostImpl::new_error( &mut self.errors, ErrorCode::RuntimeError, - format!("Only OpenVINO encoding is supported"), + "Only OpenVINO encoding is supported".to_string(), )); } // Read the guest array. let graph_internal_data = GraphInternalData { xml: graph[0].clone(), weights: graph[1].clone(), - target: target, + target, }; match self.graphs.push(graph_internal_data) { - Ok(graph_rep) => { - return Ok(Resource::::new_own(graph_rep)); - } + Ok(graph_rep) => Ok(Resource::::new_own(graph_rep)), Err(err) => { match self.errors.push(ErrorInternalData { code: ErrorCode::RuntimeError, message: format!("{:?}", err), }) { - Ok(error_rep) => { - return Err(Resource::::new_own(error_rep)); - } + Ok(error_rep) => Err(Resource::::new_own(error_rep)), Err(err) => { panic!("Can't create internal error for {:?}", err); } diff --git a/crates/doctor/src/test.rs b/crates/doctor/src/test.rs index 544eae1b4d..4fa07da64f 100644 --- a/crates/doctor/src/test.rs +++ b/crates/doctor/src/test.rs @@ -1,7 +1,7 @@ #![cfg(test)] #![allow(clippy::expect_fun_call)] -use std::{fs, io::Write, path::Path}; +use std::{io::Write, path::Path}; use tempfile::{NamedTempFile, TempPath}; use toml::Value; diff --git a/crates/locked-app/src/locked.rs b/crates/locked-app/src/locked.rs index 7dd4c5a2f7..1a1ab226af 100644 --- a/crates/locked-app/src/locked.rs +++ b/crates/locked-app/src/locked.rs @@ -294,8 +294,6 @@ pub struct Variable { mod test { use super::*; - use crate::values::ValuesMapBuilder; - #[test] fn locked_app_with_no_host_reqs_serialises_as_v0_and_v0_deserialises_as_v1() { let locked_app = LockedApp { diff --git a/examples/spin-timer/Cargo.lock b/examples/spin-timer/Cargo.lock index 60dfbe4b4e..0fda9848b6 100644 --- a/examples/spin-timer/Cargo.lock +++ b/examples/spin-timer/Cargo.lock @@ -1373,6 +1373,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2 0.5.5", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "rustls-ffi", + "vcpkg", + "windows-sys 0.52.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -1753,6 +1784,19 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1930,7 +1974,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -2444,6 +2488,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -2732,6 +2782,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -3003,6 +3064,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-sys" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3666,6 +3739,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -3942,6 +4036,39 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "openvino" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24bd3a7ef39968e6a4f1b1206c1c876f9bd50cf739ccbcd69f8539bbac5dcc7a" +dependencies = [ + "openvino-finder", + "openvino-sys", + "thiserror", +] + +[[package]] +name = "openvino-finder" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d234d1394a413ea8adaf0c40806b9ad1946be6310b441f688840654a331973" +dependencies = [ + "cfg-if", + "log", +] + +[[package]] +name = "openvino-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c98acf37fc84ad9d7da4dc6c18f0f60ad209b43a6f555be01f9003d0a2a43d" +dependencies = [ + "env_logger", + "libloading", + "once_cell", + "openvino-finder", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4988,6 +5115,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.8" @@ -4998,8 +5140,8 @@ dependencies = [ "cfg-if", "getrandom 0.2.12", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -5126,6 +5268,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.21.10" @@ -5133,7 +5287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] @@ -5145,13 +5299,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", "subtle", "zeroize", ] +[[package]] +name = "rustls-ffi" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +dependencies = [ + "libc", + "log", + "num_enum", + "rustls 0.20.9", + "rustls-pemfile 0.2.1", + "sct", + "webpki", +] + [[package]] name = "rustls-native-certs" version = "0.7.0" @@ -5165,6 +5334,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -5196,8 +5374,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5206,9 +5384,9 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "ring", + "ring 0.17.8", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -5269,8 +5447,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5689,6 +5867,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -5748,11 +5932,14 @@ dependencies = [ "cap-primitives", "cap-std", "crossbeam-channel", + "curl", "http 1.1.0", "io-extras", + "openvino", "rustix 0.37.27", "spin-telemetry", "system-interface", + "table", "tokio", "tracing", "wasi-common", @@ -6937,6 +7124,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -7854,6 +8047,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + [[package]] name = "webpki-roots" version = "0.25.4" From 12ef4e6ba73641878bed9ddde5ef780c59926150 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 1 Jul 2024 14:43:10 +0300 Subject: [PATCH 24/26] Remove hello component --- crates/core/tests/core-wasi-test/src/main.rs | 13 -------- .../core/tests/core-wasi-test/wit/hello.wit | 9 ------ crates/core/tests/hello_component.rs | 32 ------------------- crates/core/tests/integration_test.rs | 25 --------------- 4 files changed, 79 deletions(-) delete mode 100644 crates/core/tests/core-wasi-test/wit/hello.wit delete mode 100644 crates/core/tests/hello_component.rs diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index 80a5ed73ae..9c7d2be664 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -10,12 +10,6 @@ mod multiplier { path: "wit/multiplier.wit" }); } -mod hello { - wit_bindgen::generate!({ - world: "hello", - path: "wit/hello.wit" - }); -} mod ml { wit_bindgen::generate!({ @@ -31,8 +25,6 @@ use imagenet::imagenet_openvino_test; use std::path::Path; -use crate::hello::test::test::gggg2::say_hello; - type Result = std::result::Result<(), Box>; fn main() -> Result { @@ -74,11 +66,6 @@ fn main() -> Result { let output = multiplier::imports::multiply(input); println!("{output}"); } - "hello" => { - let input: String = args.next().expect("input").parse().expect("String"); - let output = say_hello(&input); - println!("{output}"); - } "imagenet" => { let path_as_string = args.next().expect("path"); let target_as_string = args.next().expect("target"); diff --git a/crates/core/tests/core-wasi-test/wit/hello.wit b/crates/core/tests/core-wasi-test/wit/hello.wit deleted file mode 100644 index 2fb999f481..0000000000 --- a/crates/core/tests/core-wasi-test/wit/hello.wit +++ /dev/null @@ -1,9 +0,0 @@ -package test:test; - -world hello { - import gggg2; -} - -interface gggg2 { - say-hello: func(x: string) -> string; -} diff --git a/crates/core/tests/hello_component.rs b/crates/core/tests/hello_component.rs deleted file mode 100644 index 3f94c243a4..0000000000 --- a/crates/core/tests/hello_component.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub mod hello { - wasmtime::component::bindgen!("hello" in "tests/core-wasi-test/wit"); - - use spin_core::HostComponent; - use test::test::gggg2; - - #[derive(Clone)] - pub struct HelloHostComponent; - - impl HostComponent for HelloHostComponent { - type Data = HelloHostImpl; - - fn add_to_linker( - linker: &mut spin_core::Linker, - get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - Hello::add_to_linker(linker, get) - } - - fn build_data(&self) -> Self::Data { - HelloHostImpl {} - } - } - - pub struct HelloHostImpl {} - - impl gggg2::Host for HelloHostImpl { - fn say_hello(&mut self, x: String) -> String { - format!("Hello bace {x}!") - } - } -} diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index b0973fc9a3..6e8efd7568 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,10 +4,8 @@ use std::{ time::{Duration, Instant}, }; -mod hello_component; mod ml_component; -use crate::hello_component::hello::{HelloHostComponent, HelloHostImpl}; use crate::ml_component::ml::MLHostComponent; use anyhow::Context; @@ -165,28 +163,6 @@ async fn test_host_component_data_update() { assert_eq!(stdout, "500"); } -#[tokio::test(flavor = "multi_thread")] -async fn test_host_component_hello() { - let engine = test_engine(); - let hello_handle = engine - .find_host_component_handle::() - .unwrap(); - - let stdout = run_core_wasi_test_engine( - &engine, - ["hello", "GGyci"], - |store_builder| { - store_builder - .host_components_data() - .set(hello_handle, HelloHostImpl {}); - }, - |_| {}, - ) - .await - .unwrap(); - assert_eq!(stdout, "Hello bace GGyci!"); -} - #[tokio::test(flavor = "multi_thread")] async fn test_host_component_imagenet_openvino_cpu() { let engine = test_engine(); @@ -256,7 +232,6 @@ fn test_config() -> Config { fn test_engine() -> Engine<()> { let mut builder = Engine::builder(&test_config()).unwrap(); builder.add_host_component(MultiplierHostComponent).unwrap(); - builder.add_host_component(HelloHostComponent).unwrap(); builder.add_host_component(MLHostComponent).unwrap(); builder From 5f167645ea1d374e2cc3ffea7ac55252f268e520 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 1 Jul 2024 15:42:50 +0300 Subject: [PATCH 25/26] Move test_host_components into directory --- crates/core/tests/integration_test.rs | 37 ++----------------- .../ml.rs} | 0 crates/core/tests/test_host_components/mod.rs | 2 + .../tests/test_host_components/multiplier.rs | 32 ++++++++++++++++ 4 files changed, 38 insertions(+), 33 deletions(-) rename crates/core/tests/{ml_component.rs => test_host_components/ml.rs} (100%) create mode 100644 crates/core/tests/test_host_components/mod.rs create mode 100644 crates/core/tests/test_host_components/multiplier.rs diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 6e8efd7568..268672824f 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,13 +4,13 @@ use std::{ time::{Duration, Instant}, }; -mod ml_component; - -use crate::ml_component::ml::MLHostComponent; +mod test_host_components; +use crate::test_host_components::ml::ml::MLHostComponent; +use crate::test_host_components::multiplier::{Multiplier, MultiplierHostComponent}; use anyhow::Context; use spin_core::{ - Component, Config, Engine, HostComponent, I32Exit, Store, StoreBuilder, Trap, WasiVersion, + Component, Config, Engine, I32Exit, Store, StoreBuilder, Trap, WasiVersion, }; use tempfile::TempDir; use tokio::{fs, io::AsyncWrite}; @@ -292,36 +292,7 @@ async fn run_core_wasi_test_engine<'a>( Ok(stdout) } -// Simple test HostComponent; multiplies the input by the configured factor -#[derive(Clone)] -struct MultiplierHostComponent; - -mod multiplier { - wasmtime::component::bindgen!("multiplier" in "tests/core-wasi-test/wit"); -} - -impl HostComponent for MultiplierHostComponent { - type Data = Multiplier; - - fn add_to_linker( - linker: &mut spin_core::Linker, - get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - multiplier::imports::add_to_linker(linker, get) - } - - fn build_data(&self) -> Self::Data { - Multiplier(2) - } -} - -struct Multiplier(i32); -impl multiplier::imports::Host for Multiplier { - fn multiply(&mut self, a: i32) -> i32 { - self.0 * a - } -} // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); diff --git a/crates/core/tests/ml_component.rs b/crates/core/tests/test_host_components/ml.rs similarity index 100% rename from crates/core/tests/ml_component.rs rename to crates/core/tests/test_host_components/ml.rs diff --git a/crates/core/tests/test_host_components/mod.rs b/crates/core/tests/test_host_components/mod.rs new file mode 100644 index 0000000000..d1fb0d3983 --- /dev/null +++ b/crates/core/tests/test_host_components/mod.rs @@ -0,0 +1,2 @@ +pub mod ml; +pub mod multiplier; \ No newline at end of file diff --git a/crates/core/tests/test_host_components/multiplier.rs b/crates/core/tests/test_host_components/multiplier.rs new file mode 100644 index 0000000000..672b7dcd43 --- /dev/null +++ b/crates/core/tests/test_host_components/multiplier.rs @@ -0,0 +1,32 @@ +use spin_core::HostComponent; + +// Simple test HostComponent; multiplies the input by the configured factor +#[derive(Clone)] +pub struct MultiplierHostComponent; + +mod multiplier { + wasmtime::component::bindgen!("multiplier" in "tests/core-wasi-test/wit"); +} + +impl HostComponent for MultiplierHostComponent { + type Data = Multiplier; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + multiplier::imports::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + Multiplier(2) + } +} + +pub struct Multiplier(pub i32); + +impl multiplier::imports::Host for Multiplier { + fn multiply(&mut self, a: i32) -> i32 { + self.0 * a + } +} \ No newline at end of file From f0eb7c521bb73965481701e5c2aca772dd1dbd41 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Mon, 1 Jul 2024 16:00:27 +0300 Subject: [PATCH 26/26] Address elapsed comment and apply fmt --- .../core/tests/core-wasi-test/src/imagenet.rs | 50 ++++++++++--------- crates/core/tests/integration_test.rs | 6 +-- crates/core/tests/test_host_components/mod.rs | 2 +- .../tests/test_host_components/multiplier.rs | 2 +- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs index 4ec5f14476..5fe02129f1 100644 --- a/crates/core/tests/core-wasi-test/src/imagenet.rs +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -3,17 +3,19 @@ use image2tensor::convert_image_to_tensor_bytes; use crate::imagenet_classes; use crate::Path; +use std::time::Duration; -pub fn elapsed_to_string(fn_name: &str, elapsed: u128) -> String { - if elapsed < 1000 { - format!("`{}` took {} ns", fn_name, elapsed) - } else if elapsed < 1000 * 1000 { - format!("`{}` took {:.2} µs", fn_name, elapsed as f64 / 1000.0) +pub fn elapsed_to_string(fn_name: &str, elapsed: &Duration) -> String { + let elapsed_ns = elapsed.as_nanos(); + if elapsed_ns < 1000 { + format!("`{}` took {} ns", fn_name, elapsed_ns) + } else if elapsed_ns < 1000 * 1000 { + format!("`{}` took {:.2} µs", fn_name, elapsed_ns as f64 / 1000.0) } else { format!( "`{}` took {:.2} ms", fn_name, - elapsed as f64 / 1000.0 / 1000.0 + elapsed_ns as f64 / 1000.0 / 1000.0 ) } } @@ -48,22 +50,22 @@ pub fn imagenet_openvino_test( let model = { let start_for_elapsed_macro = std::time::Instant::now(); let model: Vec = std::fs::read(path.join("model.xml"))?; - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Loaded model from xml {} {}", bytes_to_string(model.len()), - elapsed_to_string("fs::read", elapsed) + elapsed_to_string("fs::read", &elapsed) ); model }; let weights = { let start_for_elapsed_macro = std::time::Instant::now(); let weights = std::fs::read(path.join("model.bin"))?; - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Loaded weigths {} {}", bytes_to_string(weights.len()), - elapsed_to_string("fs::read", elapsed) + elapsed_to_string("fs::read", &elapsed) ); weights }; @@ -71,23 +73,23 @@ pub fn imagenet_openvino_test( let start_for_elapsed_macro = std::time::Instant::now(); let imagenet_graph = graph::load(&[model, weights], graph::GraphEncoding::Openvino, target).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!("---- {:?} ----", target); eprintln!( "Loaded graph with ID: {:?} {}", imagenet_graph, - elapsed_to_string("graph::load", elapsed) + elapsed_to_string("graph::load", &elapsed) ); imagenet_graph }; let context = { let start_for_elapsed_macro = std::time::Instant::now(); let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Created context with ID: {:?} {}", context, - elapsed_to_string("Graph::init_execution_context", elapsed) + elapsed_to_string("Graph::init_execution_context", &elapsed) ); context }; @@ -106,11 +108,11 @@ pub fn imagenet_openvino_test( let start_for_elapsed_macro = std::time::Instant::now(); let tensor_type = tensor::TensorType::Fp32; let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Created tensor with ID: {:?} {}", tensor_id, - elapsed_to_string("Tensor::new", elapsed) + elapsed_to_string("Tensor::new", &elapsed) ); tensor_id }; @@ -118,29 +120,29 @@ pub fn imagenet_openvino_test( { let start_for_elapsed_macro = std::time::Instant::now(); inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Input set {}", - elapsed_to_string("GraphExecutionContext::set_input", elapsed) + elapsed_to_string("GraphExecutionContext::set_input", &elapsed) ); } { let start_for_elapsed_macro = std::time::Instant::now(); inference::GraphExecutionContext::compute(&context).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Executed graph inference. {}", - elapsed_to_string("GraphExecutionContext::compute", elapsed) + elapsed_to_string("GraphExecutionContext::compute", &elapsed) ); } let output_result_id = { let start_for_elapsed_macro = std::time::Instant::now(); let output_result_id = inference::GraphExecutionContext::get_output(&context, input_name).unwrap(); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Obtaining output {}", - elapsed_to_string("GraphExecutionContext::get_output", elapsed) + elapsed_to_string("GraphExecutionContext::get_output", &elapsed) ); output_result_id }; @@ -149,10 +151,10 @@ pub fn imagenet_openvino_test( let output_data = tensor::Tensor::data(&output_result_id); let output_dimensions = tensor::Tensor::dimensions(&output_result_id); let output_type = tensor::Tensor::ty(&output_result_id); - let elapsed = start_for_elapsed_macro.elapsed().as_nanos(); + let elapsed = start_for_elapsed_macro.elapsed(); eprintln!( "Copying data from tensor. {}", - elapsed_to_string("Tensor::data+dimensions+type", elapsed) + elapsed_to_string("Tensor::data+dimensions+type", &elapsed) ); (output_data, output_dimensions, output_type) }; diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 268672824f..ba66361714 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -9,9 +9,7 @@ use crate::test_host_components::ml::ml::MLHostComponent; use crate::test_host_components::multiplier::{Multiplier, MultiplierHostComponent}; use anyhow::Context; -use spin_core::{ - Component, Config, Engine, I32Exit, Store, StoreBuilder, Trap, WasiVersion, -}; +use spin_core::{Component, Config, Engine, I32Exit, Store, StoreBuilder, Trap, WasiVersion}; use tempfile::TempDir; use tokio::{fs, io::AsyncWrite}; @@ -292,8 +290,6 @@ async fn run_core_wasi_test_engine<'a>( Ok(stdout) } - - // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); diff --git a/crates/core/tests/test_host_components/mod.rs b/crates/core/tests/test_host_components/mod.rs index d1fb0d3983..62a1ead4cb 100644 --- a/crates/core/tests/test_host_components/mod.rs +++ b/crates/core/tests/test_host_components/mod.rs @@ -1,2 +1,2 @@ pub mod ml; -pub mod multiplier; \ No newline at end of file +pub mod multiplier; diff --git a/crates/core/tests/test_host_components/multiplier.rs b/crates/core/tests/test_host_components/multiplier.rs index 672b7dcd43..b744c74df3 100644 --- a/crates/core/tests/test_host_components/multiplier.rs +++ b/crates/core/tests/test_host_components/multiplier.rs @@ -29,4 +29,4 @@ impl multiplier::imports::Host for Multiplier { fn multiply(&mut self, a: i32) -> i32 { self.0 * a } -} \ No newline at end of file +}