Skip to content

Commit

Permalink
wasi-nn: turn it on by default (#2859)
Browse files Browse the repository at this point in the history
* wasi-nn: turn it on by default

This change makes the wasi-nn Cargo feature a default feature. Previously, a wasi-nn user would have to build a separate Wasmtime binary (e.g. `cargo build --features wasi-nn ...`) to use wasi-nn and the resulting binary would require OpenVINO shared libraries to be present in the environment in order to run (otherwise it would fail immediately with linking errors). With recent changes to the `openvino` crate, the wasi-nn implementation can defer the loading of the OpenVINO shared libraries until runtime (i.e., when the user Wasm program calls `wasi_ephemeral_nn::load`) and display a user-level error if anything goes wrong (e.g., the OpenVINO libraries are not present on the system). This runtime-linking addition allows the wasi-nn feature to be turned on by default and shipped with upcoming releases of Wasmtime. This change should be transparent for users who do not use wasi-nn: the `openvino` crate is small and the newly-available wasi-nn imports only affect programs in which they are used.

For those interested in reviewing the runtime linking approach added to the `openvino` crate, see intel/openvino-rs#19.

* wasi-nn spec path: don't use canonicalize

* Allow dependencies using the ISC license

The ISC license should be [just as permissive](https://choosealicense.com/licenses/isc) as MIT, e.g., with no additional limitations.

* Add a `--wasi-modules` flag

This flag controls which WASI modules are made available to the Wasm program. This initial commit enables `wasi-common` by default (equivalent to `--wasi-modules=all`) and allows `wasi-nn` and `wasi-crypto` to be added in either individually (e.g., `--wasi-modules=wasi-nn`) or as a group (e.g., `--wasi-modules=all-experimental`).

* wasi-crypto: fix unused dependency

Co-authored-by: Pat Hickey <pat@moreproductive.org>
  • Loading branch information
abrown and pchickey authored Apr 29, 2021
1 parent ff2529c commit 92e0b6b
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 80 deletions.
53 changes: 14 additions & 39 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ members = [
]

[features]
default = ["jitdump", "wasmtime/wat", "wasmtime/parallel-compilation"]
default = ["jitdump", "wasmtime/wat", "wasmtime/parallel-compilation", "wasi-nn"]
lightbeam = ["wasmtime/lightbeam"]
jitdump = ["wasmtime/jitdump"]
vtune = ["wasmtime/vtune"]
Expand Down
2 changes: 1 addition & 1 deletion ci/run-wasi-crypto-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ pushd "$RUST_BINDINGS"
cargo build --release --target=wasm32-wasi
popd

cargo run --features wasi-crypto -- run "$RUST_BINDINGS/target/wasm32-wasi/release/wasi-crypto-guest.wasm"
cargo run --features wasi-crypto -- run "$RUST_BINDINGS/target/wasm32-wasi/release/wasi-crypto-guest.wasm" --wasi-modules=experimental-wasi-crypto
2 changes: 1 addition & 1 deletion ci/run-wasi-nn-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cp target/wasm32-wasi/release/wasi-nn-example.wasm $TMP_DIR
popd

# Run the example in Wasmtime (note that the example uses `fixture` as the expected location of the model/tensor files).
OPENVINO_INSTALL_DIR=/opt/intel/openvino cargo run --features wasi-nn -- run --mapdir fixture::$TMP_DIR $TMP_DIR/wasi-nn-example.wasm
cargo run -- run --mapdir fixture::$TMP_DIR $TMP_DIR/wasi-nn-example.wasm --wasi-modules=experimental-wasi-nn

# Clean up the temporary directory only if it was not specified (users may want to keep the directory around).
if [[ $REMOVE_TMP_DIR -eq 1 ]]; then
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-crypto/src/wiggle_interfaces/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{guest_types, WasiCryptoCtx};
use super::guest_types;

use std::num::TryFromIntError;
use wasi_crypto::CryptoError;
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-nn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ wasmtime-wasi = { path = "../wasi", version = "0.26.0" }
wiggle = { path = "../wiggle", version = "0.26.0" }

# These dependencies are necessary for the wasi-nn implementation:
openvino = "0.1.5"
openvino = { version = "0.3.1", features = ["runtime-linking"] }
thiserror = "1.0"

[build-dependencies]
Expand Down
6 changes: 2 additions & 4 deletions crates/wasi-nn/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
//! This build script:
//! - has the configuration necessary for the wiggle and witx macros.

use std::path::PathBuf;

fn main() {
// This is necessary for Wiggle/Witx macros.
let wasi_root = PathBuf::from("./spec").canonicalize().unwrap();
let cwd = std::env::current_dir().unwrap();
let wasi_root = cwd.join("spec");
println!("cargo:rustc-env=WASI_ROOT={}", wasi_root.display());

// Also automatically rebuild if the Witx files change
Expand Down
12 changes: 7 additions & 5 deletions crates/wasi-nn/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! wasi-nn API.
use crate::r#impl::UsageError;
use crate::witx::types::{Graph, GraphExecutionContext};
use openvino::InferenceError;
use openvino::{InferenceError, SetupError};
use std::cell::RefCell;
use std::collections::HashMap;
use std::hash::Hash;
Expand All @@ -14,8 +14,10 @@ use wiggle::GuestError;
pub enum WasiNnError {
#[error("guest error")]
GuestError(#[from] GuestError),
#[error("openvino error")]
OpenvinoError(#[from] InferenceError),
#[error("openvino inference error")]
OpenvinoInferenceError(#[from] InferenceError),
#[error("openvino setup error")]
OpenvinoSetupError(#[from] SetupError),
#[error("usage error")]
UsageError(#[from] UsageError),
}
Expand Down Expand Up @@ -74,7 +76,7 @@ impl ExecutionContext {

/// Capture the state necessary for calling into `openvino`.
pub struct Ctx {
pub(crate) core: openvino::Core,
pub(crate) core: Option<openvino::Core>,
pub(crate) graphs: Table<Graph, (openvino::CNNNetwork, openvino::ExecutableNetwork)>,
pub(crate) executions: Table<GraphExecutionContext, ExecutionContext>,
}
Expand All @@ -83,7 +85,7 @@ impl Ctx {
/// Make a new `WasiNnCtx` with the default settings.
pub fn new() -> WasiNnResult<Self> {
Ok(Self {
core: openvino::Core::new(None)?,
core: Option::default(),
graphs: Table::default(),
executions: Table::default(),
})
Expand Down
20 changes: 19 additions & 1 deletion crates/wasi-nn/src/impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use wiggle::GuestPtr;

#[derive(Debug, Error)]
pub enum UsageError {
#[error("Invalid context; has the load function been called?")]
InvalidContext,
#[error("Only OpenVINO's IR is currently supported, passed encoding: {0:?}")]
InvalidEncoding(GraphEncoding),
#[error("OpenVINO expects only two buffers (i.e. [ir, weights]), passed: {0}")]
Expand All @@ -34,21 +36,37 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
if encoding != GraphEncoding::Openvino {
return Err(UsageError::InvalidEncoding(encoding).into());
}

if builders.len() != 2 {
return Err(UsageError::InvalidNumberOfBuilders(builders.len()).into());
}

// 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.ctx.borrow().core.is_none() {
self.ctx
.borrow_mut()
.core
.replace(openvino::Core::new(None)?);
}

let builders = builders.as_ptr();
let xml = builders.read()?.as_slice()?;
let weights = builders.add(1)?.read()?.as_slice()?;
let graph = self
.ctx
.borrow_mut()
.core
.as_mut()
.ok_or(UsageError::InvalidContext)?
.read_network_from_buffer(&xml, &weights)?;
let executable_graph = self
.ctx
.borrow_mut()
.core
.as_mut()
.ok_or(UsageError::InvalidContext)?
.load_network(&graph, map_execution_target_to_string(target))?;
let id = self
.ctx
Expand Down Expand Up @@ -94,7 +112,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
.dimensions
.as_slice()?
.iter()
.map(|d| *d as u64)
.map(|d| *d as usize)
.collect::<Vec<_>>();
let precision = match tensor.type_ {
TensorType::F16 => Precision::FP16,
Expand Down
3 changes: 2 additions & 1 deletion crates/wasi-nn/src/witx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ impl<'a> types::UserErrorConversion for WasiNnCtx {
fn nn_errno_from_wasi_nn_error(&self, e: WasiNnError) -> Result<NnErrno, wiggle::Trap> {
eprintln!("Host error: {:?}", e);
match e {
WasiNnError::OpenvinoError(_) => unimplemented!(),
WasiNnError::OpenvinoSetupError(_) => unimplemented!(),
WasiNnError::OpenvinoInferenceError(_) => unimplemented!(),
WasiNnError::GuestError(_) => unimplemented!(),
WasiNnError::UsageError(_) => unimplemented!(),
}
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ allow = [
"Apache-2.0",
"BSD-2-Clause",
"CC0-1.0",
"ISC",
"MIT",
"MPL-2.0",
"Zlib",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ lazy_static::lazy_static! {
Compiling for a specific platform (Linux) and CPU preset (Skylake):\n\
\n \
wasmtime compile --target x86_64-unknown-linux --cranelift-enable skylake foo.wasm\n",
crate::WASM_FEATURES.as_str()
crate::FLAG_EXPLANATIONS.as_str()
)
};
}
Expand Down
60 changes: 41 additions & 19 deletions src/commands/run.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The module that implements the `wasmtime run` command.

use crate::CommonOptions;
use crate::{CommonOptions, WasiModules};
use anyhow::{bail, Context as _, Result};
use std::thread;
use std::time::Duration;
Expand Down Expand Up @@ -70,7 +70,7 @@ fn parse_preloads(s: &str) -> Result<(String, PathBuf)> {

lazy_static::lazy_static! {
static ref AFTER_HELP: String = {
crate::WASM_FEATURES.to_string()
crate::FLAG_EXPLANATIONS.to_string()
};
}

Expand Down Expand Up @@ -146,7 +146,13 @@ impl RunCommand {
let argv = self.compute_argv();

let mut linker = Linker::new(&store);
populate_with_wasi(&mut linker, preopen_dirs, &argv, &self.vars)?;
populate_with_wasi(
&mut linker,
preopen_dirs,
&argv,
&self.vars,
&self.common.wasi_modules.unwrap_or(WasiModules::default()),
)?;

// Load the preload wasm modules.
for (name, path) in self.preloads.iter() {
Expand Down Expand Up @@ -351,6 +357,7 @@ fn populate_with_wasi(
preopen_dirs: Vec<(String, Dir)>,
argv: &[String],
vars: &[(String, String)],
wasi_modules: &WasiModules,
) -> Result<()> {
// Add the current snapshot to the linker.
let mut builder = WasiCtxBuilder::new();
Expand All @@ -360,25 +367,40 @@ fn populate_with_wasi(
builder = builder.preopened_dir(dir, name)?;
}

Wasi::new(linker.store(), builder.build()?).add_to_linker(linker)?;
if wasi_modules.wasi_common {
Wasi::new(linker.store(), builder.build()?).add_to_linker(linker)?;
}

#[cfg(feature = "wasi-nn")]
{
use std::cell::RefCell;
use std::rc::Rc;
let wasi_nn = WasiNn::new(linker.store(), Rc::new(RefCell::new(WasiNnCtx::new()?)));
wasi_nn.add_to_linker(linker)?;
if wasi_modules.wasi_nn {
#[cfg(not(feature = "wasi-nn"))]
{
bail!("Cannot enable wasi-nn when the binary is not compiled with this feature.");
}
#[cfg(feature = "wasi-nn")]
{
use std::cell::RefCell;
use std::rc::Rc;
let wasi_nn = WasiNn::new(linker.store(), Rc::new(RefCell::new(WasiNnCtx::new()?)));
wasi_nn.add_to_linker(linker)?;
}
}

#[cfg(feature = "wasi-crypto")]
{
use std::cell::RefCell;
use std::rc::Rc;
let cx_crypto = Rc::new(RefCell::new(WasiCryptoCtx::new()));
WasiCryptoCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoAsymmetricCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoSignatures::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoSymmetric::new(linker.store(), cx_crypto).add_to_linker(linker)?;
if wasi_modules.wasi_crypto {
#[cfg(not(feature = "wasi-crypto"))]
{
bail!("Cannot enable wasi-crypto when the binary is not compiled with this feature.");
}
#[cfg(feature = "wasi-crypto")]
{
use std::cell::RefCell;
use std::rc::Rc;
let cx_crypto = Rc::new(RefCell::new(WasiCryptoCtx::new()));
WasiCryptoCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoAsymmetricCommon::new(linker.store(), cx_crypto.clone())
.add_to_linker(linker)?;
WasiCryptoSignatures::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoSymmetric::new(linker.store(), cx_crypto).add_to_linker(linker)?;
}
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion src/commands/wasm2obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ lazy_static::lazy_static! {
The default is a dummy environment that produces placeholder values.\n\
\n\
{}",
crate::WASM_FEATURES.as_str()
crate::FLAG_EXPLANATIONS.as_str()
)
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/wast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use wasmtime_wast::WastContext;

lazy_static::lazy_static! {
static ref AFTER_HELP: String = {
crate::WASM_FEATURES.to_string()
crate::FLAG_EXPLANATIONS.to_string()
};
}

Expand Down
Loading

0 comments on commit 92e0b6b

Please sign in to comment.