diff --git a/apps/README.md b/apps/README.md index cad9b30220730..bf8d95276af39 100644 --- a/apps/README.md +++ b/apps/README.md @@ -26,4 +26,4 @@ If you are interested in writing optimized kernels with TVM, checkout [TOPI: TVM - [android_rpc](android_rpc) Android RPC server. - [benchmark](benchmark) Example end to end compilation benchmarks - [howto_deploy](howto_deploy) Tutorial on how to deploy TVM with minimum code dependency. -- [wasm-dlbackend-tvm](wasm-dlbackend-tvm) WebAssembly backend for deep learning framework with TVM runtime. +- [wasm-graphcompiler-tvm](wasm-graphcompiler-tvm) WebAssembly graph compiler for deep learning framework with TVM runtime. diff --git a/apps/wasm-dlbackend-tvm/README.md b/apps/wasm-dlbackend-tvm/README.md deleted file mode 100644 index 42e29081606a8..0000000000000 --- a/apps/wasm-dlbackend-tvm/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# WebAssembly Backend for Deep Learning Framework with TVM Runtime - -#### Experimental notice: This project is still *experimental* and only serves as a proof of concept for running deep learning frameworks (such like [MindSpore](https://github.com/mindspore-ai/mindspore)) on [WebAssembly runtime](https://github.com/bytecodealliance/wasmtime) with [TVM stack](https://tvm.apache.org/). - -- [WebAssembly Backend for Deep Learning Framework with TVM Runtime](#webassembly-backend-for-deep-learning-framework-with-tvm-runtime) - - [Motivation](#motivation) - - [Framework Landscape](#framework-landscape) - - [Project Status](#project-status) - - [PoC Guidelines](#poc-guidelines) - - [Pre-installation](#pre-installation) - - [Build wasm-dlbackend-tvm package](#build-wasm-dlbackend-tvm-package) - - [Test](#test) - - [Future Work](#future-work) - - [Operator enhancement](#operator-enhancement) - - [Performance benchmark](#performance-benchmark) - - [Native TVM Rust runtime support](#native-tvm-rust-runtime-support) - - [Appendix](#appendix) - - [System packages install](#system-packages-install) - - [Contribution](#contribution) - -## Motivation - -TVM hardware support - -As demonstrated in TVM runtime [tutorials](https://tvm.apache.org/docs/tutorials/relay_quick_start.html), TVM already supports WASM as the optional hardware backend, so we can leverage the features of WebAssembly (portability, security) and TVM runtime (domain-specific, optimization) to build a flexible and auto-optimized operator backend for all deep learning frameworks. - -## Framework Landscape - -The figure below demonstrates the whole landscape of running deep learning framework on WASM runtime with TVM compiler stack. -``` - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ -| | _ _ _ _ _ _ _ _ _ _ _ -| Framework Frontend Expression | | | -|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| | TVM (TE) Python API | - || |_ _ _ _ _ _ _ _ _ _ _| - \/ || - _ _ _ _ _ _ _ _ _ _ _ _ _ _ \/ - | | _ _ _ _ _ _ _ _ _ _ _ - | Framework WASM Backend | | | - | (WASM runtime) | | TVM Compiler Stack | - |_ _ _ _ _ _ _ _ _ _ _ _ _ _| |_ _ _ _ _ _ _ _ _ _ _| - || || - \/ \/ - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - | | | | llvm-ar | | - | TVM Runtime | <--- | libops_wasm32.a | <------- | add.o sub.o | - |_ _ _ _ _ _ _ _| |_ _ _ _ _ _ _ _ _| |_ _ _ _ _ _ _| -``` - -## Project Status - -This project should be considered **experimental** at the very early stage, all rich features are under active development. Here is the current operator support matrix: - -| Operator Name | FP32 | INT32 | INT8 | -| ------------- | ---- | ----- | ---- | -| Add | ✔️ |
|
| -| Sub | ✔️ |
|
| - -**NOTICE**: Currently this project is ONLY tested on Ubuntu system, so `Ubuntu 16.04+` should be prepared as the testing environment. - -## PoC Guidelines - -### Pre-installation - -* Rust - - Before running this demo, please make sure [Rust](#system-packages-install) has been installed. - - After Rust installed, execute the code below to add `wasm32-wasi` target: - ```shell - rustup target add wasm32-wasi - cargo install cargo-wasi - ``` - -* TVM - - Please follow TVM [installations](https://tvm.apache.org/docs/install/index.html), `export TVM_HOME=/path/to/tvm` and add `libtvm_runtime` to your `LD_LIBRARY_PATH`. - - *Note:* To run the end-to-end examples and tests, `tvm` and `topi` need to be added to your `PYTHONPATH` or it's automatic via an Anaconda environment when it is installed individually. - -### Build wasm-dlbackend-tvm package - -```shell -cd wasm-dlbackend-tvm && cargo wasi build --release -``` - -### Test - -Run the command below to install the frontend package for testing (`rust` REQUIRED): -```shell -cd wasm-dlfrontend/ && cargo build --release -cp ./target/release/wasm-dlfrontend /usr/local/bin/ -``` - -Check the usage of `wasm-dlfrontend`: - -```shell -~# wasm-dlfrontend -h - -Usage: wasm-dlfrontend [options] - -Options: - -c, --ms-backend-config FILE_PATH - set wasm backend config file - -o, --op-type VALUE set the operator type, currently ONLY support Add and - Sub, default: Add. - -h, --help print this help menu -``` - -## Future Work - -### Operator enhancement -TODO - -### Performance benchmark -TODO - -### Native TVM Rust runtime support -TODO - -## Appendix - -### System packages install - -* Rust (latest version) - - If you are running Windows, to install Rust, download and run the [RUST-INIT.EXE](https://win.rustup.rs/), and then follow the onscreen instructions. - - If you are a Linux user, run the following in your terminal, then follow the on-screen instructions to install Rust. - - ```shell - curl https://sh.rustup.rs -sSf | sh - ``` - -## Contribution - -Lastly very thanks [@kazum](https://github.com/kazum) for having offered a lot of help when implementing this project. diff --git a/apps/wasm-dlbackend-tvm/build.rs b/apps/wasm-dlbackend-tvm/build.rs deleted file mode 100644 index 20507fd0dd545..0000000000000 --- a/apps/wasm-dlbackend-tvm/build.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::{path::PathBuf, process::Command}; - -fn main() { - let mut out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - out_dir.push("lib"); - - if !out_dir.is_dir() { - std::fs::create_dir(&out_dir).unwrap(); - } - - Command::new(concat!( - env!("CARGO_MANIFEST_DIR"), - "/tools/build_ops_lib.py" - )) - .arg(&out_dir) - .output() - .expect("Failed to execute command!"); - - let ar = option_env!("LLVM_AR").unwrap_or("llvm-ar-10"); - let add_obj_file = out_dir.join("add.o"); - let sub_obj_file = out_dir.join("sub.o"); - let lib_file = out_dir.join("libops_wasm32.a"); - Command::new(ar) - .arg("rcs") - .arg(&lib_file) - .arg(&add_obj_file) - .arg(&sub_obj_file) - .output() - .expect("Failed to execute command!"); - - println!("cargo:rustc-link-search=native={}", out_dir.display()); -} diff --git a/apps/wasm-dlbackend-tvm/src/lib.rs b/apps/wasm-dlbackend-tvm/src/lib.rs deleted file mode 100644 index acc1937da4153..0000000000000 --- a/apps/wasm-dlbackend-tvm/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate tvm_runtime; - -mod ops; -use ops::types::Status; -mod utils; - -#[no_mangle] -pub extern "C" fn run(op_type: i32, in_addr: i32, in_size: i32, out_addr: i32) -> i32 { - let inputs = utils::load_inputs(in_addr, in_size as usize); - if ops::validate_inputs(&inputs) != Status::Succeed { - return 0i32; - } - - let op_instance = ops::operator_instantiate(op_type); - let (a_shape, b_shape, c_shape) = ops::parse_inputs_shape(&inputs); - if op_instance.init(a_shape, b_shape, c_shape) != Status::Succeed { - return 0i32; - }; - - let (in_tensors, out_tensor) = ops::parse_inputs_tensor(&inputs); - let (stat, output) = op_instance.launch(in_tensors, out_tensor); - if stat != Status::Succeed { - return 0i32; - } - - let out_size = utils::store_output(out_addr, output); - out_size as i32 -} diff --git a/apps/wasm-dlbackend-tvm/src/ops/add.rs b/apps/wasm-dlbackend-tvm/src/ops/add.rs deleted file mode 100644 index cf9320be691ec..0000000000000 --- a/apps/wasm-dlbackend-tvm/src/ops/add.rs +++ /dev/null @@ -1,63 +0,0 @@ -use super::types::*; -use tvm_runtime::{Module as _, SystemLibModule}; - -extern "C" { - fn __wasm_call_ctors(); -} - -pub struct TVMAddOp {} - -impl TVMAddOp { - pub fn new() -> Self { - Self {} - } -} - -impl Operator for TVMAddOp { - fn init(&self, a_shape: Vec, b_shape: Vec, c_shape: Vec) -> Status { - if !((a_shape.len() == b_shape.len() - && a_shape - .iter() - .zip(&b_shape) - .filter(|&(a, b)| a == b) - .count() - == a_shape.len()) - && (b_shape.len() == c_shape.len() - && b_shape - .iter() - .zip(&c_shape) - .filter(|&(b, c)| b == c) - .count() - == c_shape.len())) - { - println!("Both dimension size and shape for Add operator should be equal!"); - return Status::InitFailed; - } - - println!("TVM Add operator init success!"); - Status::Succeed - } - - fn launch(&self, mut inputs: Vec, output: Tensor) -> (Status, Tensor) { - if inputs.len() != 2 { - println!("Inputs tensor length should be 2!"); - return (Status::LaunchFailed, Tensor::default()); - } - let mut l_tensor = inputs.get_mut(0).unwrap().as_dltensor(); - let mut r_tensor = inputs.get_mut(1).unwrap().as_dltensor(); - let mut out_tensor = output.as_dltensor(); - - unsafe { - // This is necessary to invoke TVMBackendRegisterSystemLibSymbol - // API calls. - __wasm_call_ctors(); - } - let syslib = SystemLibModule::default(); - let add = syslib.get_function("add").expect("add function not found!"); - call_packed!(add, &mut l_tensor, &mut r_tensor, &mut out_tensor).unwrap(); - - let output: Tensor = out_tensor.into(); - println!("TVM Add operator run success!"); - (Status::Succeed, output) - } -} diff --git a/apps/wasm-dlbackend-tvm/src/ops/mod.rs b/apps/wasm-dlbackend-tvm/src/ops/mod.rs deleted file mode 100644 index c4188b46fcafc..0000000000000 --- a/apps/wasm-dlbackend-tvm/src/ops/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -mod add; -use add::TVMAddOp; -mod sub; -use sub::TVMSubOp; -pub mod types; -use types::*; - -use std::boxed::Box; - -pub fn operator_instantiate(op_type: i32) -> Box { - match OpType::from(op_type) { - OpType::Add => Box::new(TVMAddOp::new()), - OpType::Sub => Box::new(TVMSubOp::new()), - } -} - -pub fn validate_inputs(inputs: &Vec) -> Status { - if (inputs.len() == 3 - && !(inputs[0].dtype() == inputs[1].dtype() && inputs[0].dtype() == inputs[2].dtype())) - || (inputs.len() == 2 && inputs[0].dtype() != inputs[1].dtype()) - { - println!("The dtype of inputs and outputs is not equal!"); - Status::ValidateFailed - } else { - Status::Succeed - } -} - -pub fn parse_inputs_shape(inputs: &Vec) -> (Vec, Vec, Vec) { - if inputs.len() == 3 { - (inputs[0].shape(), inputs[1].shape(), inputs[2].shape()) - } else { - (inputs[0].shape(), inputs[1].shape(), Vec::new()) - } -} - -pub fn parse_inputs_tensor(inputs: &Vec) -> (Vec, Tensor) { - if inputs.len() == 3 { - ( - vec![inputs[0].clone(), inputs[1].clone()], - inputs[2].clone(), - ) - } else { - (vec![inputs[0].clone()], inputs[1].clone()) - } -} diff --git a/apps/wasm-dlbackend-tvm/src/ops/sub.rs b/apps/wasm-dlbackend-tvm/src/ops/sub.rs deleted file mode 100644 index 6e1f3aac78c9e..0000000000000 --- a/apps/wasm-dlbackend-tvm/src/ops/sub.rs +++ /dev/null @@ -1,63 +0,0 @@ -use super::types::*; -use tvm_runtime::{Module as _, SystemLibModule}; - -extern "C" { - fn __wasm_call_ctors(); -} - -pub struct TVMSubOp {} - -impl TVMSubOp { - pub fn new() -> Self { - Self {} - } -} - -impl Operator for TVMSubOp { - fn init(&self, a_shape: Vec, b_shape: Vec, c_shape: Vec) -> Status { - if !((a_shape.len() == b_shape.len() - && a_shape - .iter() - .zip(&b_shape) - .filter(|&(a, b)| a == b) - .count() - == a_shape.len()) - && (b_shape.len() == c_shape.len() - && b_shape - .iter() - .zip(&c_shape) - .filter(|&(b, c)| b == c) - .count() - == c_shape.len())) - { - println!("Both dimension size and shape for Sub operator should be equal!"); - return Status::InitFailed; - } - - println!("TVM Sub operator init success!"); - Status::Succeed - } - - fn launch(&self, mut inputs: Vec, output: Tensor) -> (Status, Tensor) { - if inputs.len() != 2 { - println!("Inputs tensor length should be 2!"); - return (Status::LaunchFailed, Tensor::default()); - } - let mut l_tensor = inputs.get_mut(0).unwrap().as_dltensor(); - let mut r_tensor = inputs.get_mut(1).unwrap().as_dltensor(); - let mut out_tensor = output.as_dltensor(); - - unsafe { - // This is necessary to invoke TVMBackendRegisterSystemLibSymbol - // API calls. - __wasm_call_ctors(); - } - let syslib = SystemLibModule::default(); - let sub = syslib.get_function("sub").expect("sub function not found!"); - call_packed!(sub, &mut l_tensor, &mut r_tensor, &mut out_tensor).unwrap(); - - let output: Tensor = out_tensor.into(); - println!("TVM Sub operator run success!"); - (Status::Succeed, output) - } -} diff --git a/apps/wasm-dlbackend-tvm/tools/build_ops_lib.py b/apps/wasm-dlbackend-tvm/tools/build_ops_lib.py deleted file mode 100644 index e7a85ea1adc73..0000000000000 --- a/apps/wasm-dlbackend-tvm/tools/build_ops_lib.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -"""Prepares a simple TVM library for operators.""" - -from os import path as osp -import sys - -import tvm -from tvm import te - - -def add(): - n = te.var('n') - A = te.placeholder((n,), name='A') - B = te.placeholder((n,), name='B') - C = te.compute(A.shape, lambda *i: A(*i) + B(*i), name='C') - s = tvm.te.create_schedule(C.op) - s[C].parallel(s[C].op.axis[0]) - m = tvm.lower(s, [A, B, C], name="add", simple_mode=True) - tvm.build(m, target="llvm -target=wasm32-unknown-unknown --system-lib").save(osp.join(sys.argv[1], 'add.o')) - - -def sub(): - n = te.var('n') - A = te.placeholder((n,), name='A') - B = te.placeholder((n,), name='B') - C = te.compute(A.shape, lambda *i: A(*i) - B(*i), name='C') - s = tvm.te.create_schedule(C.op) - s[C].parallel(s[C].op.axis[0]) - m = tvm.lower(s, [A, B, C], name="sub", simple_mode=True) - tvm.build(m, target="llvm -target=wasm32-unknown-unknown --system-lib").save(osp.join(sys.argv[1], 'sub.o')) - - -if __name__ == '__main__': - add() - sub() diff --git a/apps/wasm-dlbackend-tvm/wasm-dlfrontend/Cargo.toml b/apps/wasm-dlbackend-tvm/wasm-dlfrontend/Cargo.toml deleted file mode 100644 index e5174f91e065c..0000000000000 --- a/apps/wasm-dlbackend-tvm/wasm-dlfrontend/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "wasm-dlfrontend" -version = "0.1.0" -authors = ["leonwanghui "] -edition = "2018" - -[dependencies] -wasmtime = "0.17.0" -wasmtime-wasi = "0.17.0" -anyhow = "1.0.30" -serde = "1.0.53" -serde_json = "1.0.53" -serde_derive = "1.0.53" -getopts = "0.2.21" -ndarray = "0.12" diff --git a/apps/wasm-dlbackend-tvm/wasm-dlfrontend/src/main.rs b/apps/wasm-dlbackend-tvm/wasm-dlfrontend/src/main.rs deleted file mode 100644 index 0c4cc5f4f9564..0000000000000 --- a/apps/wasm-dlbackend-tvm/wasm-dlfrontend/src/main.rs +++ /dev/null @@ -1,132 +0,0 @@ -#[macro_use] -extern crate serde_derive; - -pub mod types; -use types::*; - -use anyhow::Result; -use getopts::Options; -use ndarray::Array; -use serde_json; -use std::env; -use wasmtime::*; -use wasmtime_wasi::{Wasi, WasiCtx}; - -fn print_usage(program: &str, opts: Options) { - let brief = format!("Usage: {} [options]", program); - print!("{}", opts.usage(&brief)); -} - -fn main() { - let args: Vec = env::args().collect(); - let program = args[0].clone(); - - let mut opts = Options::new(); - opts.optopt( - "c", - "ms-backend-config", - "set wasm backend config file", - "FILE_PATH", - ); - opts.optopt( - "o", - "op-type", - "set the operator type, currently ONLY support Add and Sub, default: Add.", - "VALUE", - ); - - opts.optflag("h", "help", "print this help menu"); - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(f) => panic!(f.to_string()), - }; - if matches.opt_present("h") { - print_usage(&program, opts); - return; - } - let wasm_backend_file: String = match matches.opt_str("c") { - Some(s) => s, - None => String::from("/opt/ms-backend-wasm/wasm_backend_tvm.wasi.wasm"), - }; - let op_type_str: String = match matches.opt_str("o") { - Some(s) => s, - None => String::from("Add"), - }; - let op_type: i32 = match op_type_str.as_str() { - "Add" => 0, - "Sub" => 1, - _ => 0, - }; - let a = Array::from_vec(vec![1f32, 2., 3., 4.]); - let b = Array::from_vec(vec![1f32; 4]); - let c = Array::from_vec(vec![0f32; 4]); - let a_tensor: Tensor = a.into(); - let b_tensor: Tensor = b.into(); - let c_tensor: Tensor = c.into(); - - let result: Tensor = match execute( - wasm_backend_file, - op_type, - vec![a_tensor, b_tensor, c_tensor], - ) { - Ok(m) => m, - Err(f) => panic!(f.to_string()), - }; - println!( - "{}", - serde_json::to_string_pretty(&result.to_vec::()).unwrap() - ); -} - -fn execute(wasm_backend_file: String, op_type: i32, input_data: Vec) -> Result { - let store = Store::default(); - - // First set up our linker which is going to be linking modules together. We - // want our linker to have wasi available, so we set that up here as well. - let mut linker = Linker::new(&store); - // Create an instance of `Wasi` which contains a `WasiCtx`. Note that - // `WasiCtx` provides a number of ways to configure what the target program - // will have access to. - let wasi = Wasi::new(&store, WasiCtx::new(std::env::args())?); - wasi.add_to_linker(&mut linker)?; - - let module = Module::from_file(store.engine(), &wasm_backend_file)?; - let instance = linker.instantiate(&module)?; - let memory = instance - .get_memory("memory") - .ok_or(anyhow::format_err!("failed to find `memory` export"))?; - - // Specify the input address and output address to access the wasm memory. - let in_addr = 0x1000; - let out_addr = 0x2000; - - // Serialize the data into a JSON string. - let in_data = serde_json::to_vec(&input_data)?; - let in_size = in_data.len(); - // Insert the input data into wasm memory. - for i in 0..in_size { - unsafe { - memory.data_unchecked_mut()[in_addr + i] = *in_data.get(i).unwrap(); - } - } - - // Invoke `run` export - let run = instance - .get_func("run") - .ok_or(anyhow::format_err!("failed to find `run` function export!"))? - .get4::()?; - - let out_size = run( - op_type.clone() as i32, - in_addr as i32, - in_size as i32, - out_addr as i32, - )?; - if out_size == 0 { - panic!("Opeartor {:?} run failed!", op_type); - } - - let out_data = unsafe { &memory.data_unchecked()[out_addr..][..out_size as usize] }; - let out_vec: Tensor = serde_json::from_slice(out_data).unwrap(); - Ok(out_vec.clone()) -} diff --git a/apps/wasm-dlbackend-tvm/.gitignore b/apps/wasm-graphcompiler-tvm/.gitignore similarity index 100% rename from apps/wasm-dlbackend-tvm/.gitignore rename to apps/wasm-graphcompiler-tvm/.gitignore diff --git a/apps/wasm-graphcompiler-tvm/README.md b/apps/wasm-graphcompiler-tvm/README.md new file mode 100644 index 0000000000000..a2b83d92f8af8 --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/README.md @@ -0,0 +1,203 @@ +# WebAssembly GraphCompiler for Deep Learning Framework with TVM Runtime + +#### Experimental notice: This project is still *experimental* and only serves as a proof of concept for running deep learning frameworks (such like [MindSpore](https://github.com/mindspore-ai/mindspore)) on [WebAssembly runtime](https://github.com/bytecodealliance/wasmtime) with [TVM stack](https://tvm.apache.org/). + +- [WebAssembly GraphCompiler for Deep Learning Framework with TVM Runtime](#webassembly-graphcompiler-for-deep-learning-framework-with-tvm-runtime) + - [Motivation](#motivation) + - [Framework Landscape](#framework-landscape) + - [Project Status](#project-status) + - [PoC Guidelines](#poc-guidelines) + - [Pre-installation](#pre-installation) + - [Build ResNet50 model](#build-resnet50-model) + - [Build wasm-graphcompiler-tvm package](#build-wasm-graphcompiler-tvm-package) + - [Test](#test) + - [Future Work](#future-work) + - [Operator enhancement](#operator-enhancement) + - [Performance benchmark](#performance-benchmark) + - [Native TVM Rust runtime support](#native-tvm-rust-runtime-support) + - [Appendix](#appendix) + - [System packages install](#system-packages-install) + - [Contribution](#contribution) + +## Motivation + +TVM hardware support + +As demonstrated in TVM runtime [tutorials](https://tvm.apache.org/docs/tutorials/relay_quick_start.html), TVM already supports WASM as the optional hardware backend, so we can leverage the features of WebAssembly (portability, security) and TVM runtime (domain-specific, optimization) to build a flexible and auto-optimized graph compiler for all deep learning frameworks. + +## Framework Landscape + +The figures below demonstrate the whole landscape of running deep learning frameworks on WASM runtime with TVM compiler stack. + +* WASM graph compiler stack + ``` + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + | | | | | | + | Framework Model | ---> | ONNX Model | ---> | TVM Relay Python API | + |_ _ _ _ _ _ _ _ _ _| |_ _ _ _ _ _ _| |_ _ _ _ _ _ _ _ _ _ _ _| + || + \/ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + | | | | + | WASM Graph Compiler | | TVM Compiler Stack | + | (TVM runtime) | |_ _ _ _ _ _ _ _ _ _ _| + |_ _ _ _ _ _ _ _ _ _ _| || + || \/ + _ _ _ _ _ _ _ _ || _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + | | \/ | | llvm-ar | | + | *.wasi.wasm | <--- | libgraph_wasm32.a | <------- | graph.o | + |_ _ _ _ _ _ _ _| |_ _ _ _ _ _ _ _ _ _| |_ _ _ _ _| + ``` + +* WASM graph runtime + ``` + _ _ _ _ _ _ _ _ _ _ _ + | | + | WASM Graph Runtime | + | (WASM runtime) | + |_ _ _ _ _ _ _ _ _ _ _| + || + _ _ _ _\/_ _ _ _ + | | + | *.wasi.wasm | + |_ _ _ _ _ _ _ _ | + ``` + +## Project Status + +This project should be considered **experimental** at the very early stage, all rich features are under active development. Here is the current operator support matrix: + +| Model Name | Status | +| ---------- | ------ | +| ResNet50 | ✔️ | +| LeNet |
| + +**NOTICE**: Currently this project is ONLY tested on Ubuntu system, so `Ubuntu 16.04+` should be prepared as the testing environment. + +## PoC Guidelines + +### Pre-installation + +* Rust + + Before running this demo, please make sure [Rust](#system-packages-install) has been installed. + + After Rust installed, execute the code below to add `wasm32-wasi` target: + ```shell + rustup target add wasm32-wasi + cargo install cargo-wasi + ``` + +* Wasmtime + + Please NOTICE that [Wasmtime](#system-packages-install) should be installed in advance. + +* TVM + + Please follow TVM [installations](https://tvm.apache.org/docs/install/index.html), `export TVM_HOME=/path/to/tvm` and add `libtvm_runtime` to your `LD_LIBRARY_PATH`. + + *Note:* To run the end-to-end examples and tests, `tvm` and `topi` need to be added to your `PYTHONPATH` or it's automatic via an Anaconda environment when it is installed individually. + +* LLVM + + `LLVM 10.0` or later is REQUIRED. + +### Build ResNet50 model + +- Build DL library in the WebAssembly format. + + - Download model + + ``` + cd wasm-graphcompiler/tools && wget https://s3.amazonaws.com/onnx-model-zoo/resnet/resnet50v1/resnet50v1.onnx + ``` + + - Compile + + ``` + LLVM_AR=llvm-ar-10 python ./build_graph_lib.py -O3 ./resnet50v1.onnx + ``` + +### Build wasm-graphcompiler-tvm package + +```shell +cd wasm-graphcompiler && cargo wasi build --release +cp ./target/wasm32-wasi/release/wasm_graphcompiler_tvm.wasi.wasm ../wasm-graphruntime/tools/ +``` + +### Test + +Before running this demo, please make sure [`Rust`](#system-packages-install) has been installed. + +Next run the command below to install the runtime package for testing (`rust` REQUIRED): + +```shell +cd wasm-graphruntime/ && cargo build --release +cp ./target/release/wasm-graphruntime /usr/local/bin/ +``` + +Check the usage of `wasm-graphruntime`: + +```shell +~# wasm-graphruntime -h + +Usage: wasm-graphruntime [options] + +Options: + -c, --ms-backend-config FILE_PATH + set wasm backend config file + -i, --input-data-file FILE_PATH + set the path to input image file + -h, --help print this help menu +``` + +Next perform model inference using these commands below: +``` +$ cd wasm-graphruntime/tools && wget -O cat.png https://github.com/dmlc/mxnet.js/blob/master/data/cat.png?raw=true +$ wasm-graphruntime -c ./wasm_graphcompiler_tvm.wasi.wasm -i ./cat.png +original image dimensions: (256, 256) +resized image dimensions: (224, 224) +input image belongs to the class `tabby, tabby cat` +``` + +## Future Work + +### More networks support +TODO + +### Performance benchmark + +We are working on several improvements on performances: +* WebAssemvly SIMD support +* Auto-tvm enhancement for llvm target + +### Native TVM Rust runtime support +TODO + +## Appendix + +### System packages install + +* Rust (latest version) + + If you are running Windows, to install Rust, download and run the [RUST-INIT.EXE](https://win.rustup.rs/), and then follow the onscreen instructions. + + If you are a Linux user, run the following in your terminal, then follow the on-screen instructions to install Rust. + + ```shell + curl https://sh.rustup.rs -sSf | sh + ``` + +* wasmtime (latest version) + + If you are running Windows 64-bit, download and run [Wasmtime Installer](https://github.com/CraneStation/wasmtime/releases/download/dev/wasmtime-dev-x86_64-windows.msi) then follow the onscreen instructions. + + If you're a Linux user run the following in your terminal, then follow the onscreen instructions to install `wasmtime`: + + ```shell + curl https://wasmtime.dev/install.sh -sSf | bash + ``` + +## Contribution + +Lastly very thanks [@kazum](https://github.com/kazum) for having offered a lot of help when implementing this project. diff --git a/apps/wasm-dlbackend-tvm/.cargo/config b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/.cargo/config similarity index 85% rename from apps/wasm-dlbackend-tvm/.cargo/config rename to apps/wasm-graphcompiler-tvm/wasm-graphcompiler/.cargo/config index e17b808e4b074..b01a37beeb907 100644 --- a/apps/wasm-dlbackend-tvm/.cargo/config +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/.cargo/config @@ -1,3 +1,3 @@ [build] target = "wasm32-wasi" -rustflags = ["-C", "link-arg=--whole-archive", "-C", "link-arg=-lops_wasm32"] +rustflags = ["-C", "link-arg=--whole-archive", "-C", "link-arg=-lgraph_wasm32"] diff --git a/apps/wasm-dlbackend-tvm/Cargo.toml b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/Cargo.toml similarity index 65% rename from apps/wasm-dlbackend-tvm/Cargo.toml rename to apps/wasm-graphcompiler-tvm/wasm-graphcompiler/Cargo.toml index 596890354f8f5..cfa046f2ba994 100644 --- a/apps/wasm-dlbackend-tvm/Cargo.toml +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "wasm-dlbackend-tvm" +name = "wasm-graphcompiler-tvm" version = "0.1.0" authors = ["leonwanghui "] edition = "2018" -description = "WebAssembly backend to deep learning framework using TVM runtime" +description = "WebAssembly graph compiler to deep learning frameworks using TVM" readme = "README.md" repository = "https://github.com/apache/incubator-tvm" license = "Apache-2.0" @@ -26,5 +26,6 @@ serde = "1.0.53" serde_derive = "1.0.53" serde_json = "1.0.53" ndarray = "0.12" -tvm-common = { version = "0.1", path = "../../rust/common" } -tvm-runtime = { version = "0.1", path = "../../rust/runtime" } +tvm-common = { version = "0.1", path = "../../../rust/common" } +tvm-runtime = { version = "0.1", path = "../../../rust/runtime" } +lazy_static = "1.1.1" diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/build.rs b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/build.rs new file mode 100644 index 0000000000000..553a017131fed --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/build.rs @@ -0,0 +1,8 @@ +use std::path::PathBuf; + +fn main() { + let mut out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + out_dir.push("lib"); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); +} diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/lib.rs b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/lib.rs new file mode 100644 index 0000000000000..11722695cd374 --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/lib.rs @@ -0,0 +1,64 @@ +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate serde_derive; +extern crate ndarray; +extern crate tvm_runtime; + +mod types; +use types::Tensor; +mod utils; + +use std::{collections::HashMap, convert::TryFrom, env, sync::Mutex}; +use tvm_runtime::{Graph, GraphExecutor, SystemLibModule, Tensor as TVMTensor}; + +extern "C" { + fn __wasm_call_ctors(); +} + +lazy_static! { + static ref SYSLIB: SystemLibModule = SystemLibModule::default(); + static ref GRAPH_EXECUTOR: Mutex> = { + unsafe { + // This is necessary to invoke TVMBackendRegisterSystemLibSymbol + // API calls. + __wasm_call_ctors(); + } + let graph = Graph::try_from(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/lib/graph.json" + ))) + .unwrap(); + let params_bytes = + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/lib/graph.params")); + let params = tvm_runtime::load_param_dict(params_bytes) + .unwrap() + .into_iter() + .map(|(k, v)| (k, v.to_owned())) + .collect::>>(); + + let mut exec = GraphExecutor::new(graph, &*SYSLIB).unwrap(); + exec.load_params(params); + + Mutex::new(exec) + }; +} + +#[no_mangle] +pub extern "C" fn run(wasm_addr: i32, in_size: i32) -> i32 { + let in_tensor = utils::load_input(wasm_addr, in_size as usize); + let input: TVMTensor = in_tensor.as_dltensor().into(); + + GRAPH_EXECUTOR.lock().unwrap().set_input("data", input); + GRAPH_EXECUTOR.lock().unwrap().run(); + let output = GRAPH_EXECUTOR + .lock() + .unwrap() + .get_output(0) + .unwrap() + .as_dltensor(false); + + let out_tensor: Tensor = output.into(); + let out_size = utils::store_output(wasm_addr, out_tensor); + out_size as i32 +} diff --git a/apps/wasm-dlbackend-tvm/src/ops/types.rs b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/types.rs similarity index 88% rename from apps/wasm-dlbackend-tvm/src/ops/types.rs rename to apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/types.rs index b87fa0de0c7a5..8f7d8e42cf5c1 100644 --- a/apps/wasm-dlbackend-tvm/src/ops/types.rs +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/types.rs @@ -9,36 +9,6 @@ use tvm_common::ffi::{ DLContext, DLDataType, DLDataTypeCode_kDLFloat, DLDataTypeCode_kDLInt, DLDeviceType_kDLCPU, }; -pub trait Operator { - fn init(&self, a_shape: Vec, b_shape: Vec, c_shape: Vec) -> Status; - - fn launch(&self, inputs: Vec, output: Tensor) -> (Status, Tensor); -} - -#[derive(Debug, PartialEq)] -pub enum Status { - Succeed, - ValidateFailed, - InitFailed, - LaunchFailed, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum OpType { - Add, - Sub, -} - -impl From for OpType { - fn from(op_type: i32) -> Self { - match op_type { - 0i32 => OpType::Add, - 1i32 => OpType::Sub, - _ => OpType::Add, - } - } -} - #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum DataType { FP32, diff --git a/apps/wasm-dlbackend-tvm/src/utils.rs b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/utils.rs similarity index 76% rename from apps/wasm-dlbackend-tvm/src/utils.rs rename to apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/utils.rs index 05050981f4760..76380103484d3 100644 --- a/apps/wasm-dlbackend-tvm/src/utils.rs +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/src/utils.rs @@ -1,17 +1,17 @@ -use super::ops::types::*; +use super::types::*; use serde_json; use std::ptr; -pub fn load_inputs(in_addr: i32, in_size: usize) -> Vec { +pub fn load_input(in_addr: i32, in_size: usize) -> Tensor { let in_addr = in_addr as *mut u8; let mut data_vec = Vec::new(); for i in 0..in_size { data_vec.push(unsafe { ptr::read(in_addr.offset(i as isize)) }); } - let inputs: Vec = serde_json::from_slice(&data_vec).unwrap(); + let input: Tensor = serde_json::from_slice(&data_vec).unwrap(); - inputs + input } pub fn store_output(out_addr: i32, output: Tensor) -> usize { diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/tools/build_graph_lib.py b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/tools/build_graph_lib.py new file mode 100644 index 0000000000000..73b1895562a80 --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphcompiler/tools/build_graph_lib.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +"""Builds a simple graph for testing.""" +import argparse +import os +import subprocess +import sys + +import onnx +import tvm +from tvm import relay + + +def _get_mod_and_params(model_file): + onnx_model = onnx.load(model_file) + shape_dict = {} + for input in onnx_model.graph.input: + shape_dict[input.name] = [dim.dim_value for dim in input.type.tensor_type.shape.dim] + + return relay.frontend.from_onnx(onnx_model, shape_dict) + + +def build_graph_lib(model_file, opt_level): + """Compiles the pre-trained model with TVM""" + out_dir = os.path.join(sys.path[0], "../lib") + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + # Compile the relay mod + mod, params = _get_mod_and_params(model_file) + with tvm.transform.PassContext(opt_level=opt_level): + graph_json, lib, params = relay.build( + mod, target="llvm -target=wasm32-unknown-unknown --system-lib", params=params) + + # Save the model artifacts to obj_file + obj_file = os.path.join(out_dir, 'graph.o') + lib.save(obj_file) + # Run llvm-ar to archive obj_file into lib_file + lib_file = os.path.join(out_dir, 'libgraph_wasm32.a') + cmds = [os.environ.get("LLVM_AR", "llvm-ar-10"), 'rcs', lib_file, obj_file] + subprocess.run(cmds) + + with open(os.path.join(out_dir, 'graph.json'), 'w') as f_graph: + f_graph.write(graph_json) + + with open(os.path.join(out_dir, 'graph.params'), 'wb') as f_params: + f_params.write(relay.save_param_dict(params)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='ONNX ResNet50 model build example') + parser.add_argument('model_file', type=str, help='the path of onnx model file') + parser.add_argument('-O', '--opt-level', type=int, default=0, + help='level of optimization. 0 is unoptimized and 3 is the highest level') + args = parser.parse_args() + + build_graph_lib(args.model_file, args.opt_level) diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphruntime/Cargo.toml b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/Cargo.toml new file mode 100644 index 0000000000000..294bdd74a72e1 --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasm-graphruntime" +version = "0.1.0" +authors = ["leonwanghui "] +edition = "2018" +description = "WebAssembly graph runtime to deep learning frameworks using wasmtime" +license = "Apache-2.0" +keywords = ["wasm", "machine learning", "wasmtime"] + +[dependencies] +wasmtime = "0.18.0" +wasmtime-wasi = "0.18.0" +anyhow = "1.0.31" +serde = "1.0.53" +serde_json = "1.0.53" +serde_derive = "1.0.53" +getopts = "0.2.21" +ndarray = "0.12" +csv = "1.1" +image = "0.20" diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphruntime/src/main.rs b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/src/main.rs new file mode 100644 index 0000000000000..179859d04c0eb --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/src/main.rs @@ -0,0 +1,173 @@ +#[macro_use] +extern crate serde_derive; +extern crate csv; +extern crate image; + +mod types; +use types::Tensor; + +use anyhow::Result; +use getopts::Options; +use image::{FilterType, GenericImageView}; +use ndarray::Array; +use serde_json; +use std::{collections::HashMap, env}; +use wasmtime::*; +use wasmtime_wasi::{Wasi, WasiCtx}; + +const IMG_HEIGHT: usize = 224; +const IMG_WIDTH: usize = 224; + +fn print_usage(program: &str, opts: Options) { + let brief = format!("Usage: {} [options]", program); + print!("{}", opts.usage(&brief)); +} + +fn main() { + let args: Vec = env::args().collect(); + let program = args[0].clone(); + + let mut opts = Options::new(); + opts.optopt( + "c", + "ms-backend-config", + "set wasm backend config file", + "FILE_PATH", + ); + opts.optopt( + "i", + "input-data-file", + "set the path to input image file", + "FILE_PATH", + ); + opts.optflag("h", "help", "print this help menu"); + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => panic!(f.to_string()), + }; + if matches.opt_present("h") { + print_usage(&program, opts); + return; + } + let wasm_backend_file = matches.opt_str("c").unwrap(); + let input_data_file = matches.opt_str("i").unwrap(); + let img = image::open(input_data_file).unwrap(); + let input = data_preprocess(img); + + let output: Tensor = match execute(wasm_backend_file, input) { + Ok(m) => m, + Err(f) => panic!(f.to_string()), + }; + output_assert(output); +} + +fn data_preprocess(img: image::DynamicImage) -> Tensor { + println!("original image dimensions: {:?}", img.dimensions()); + let img = img + .resize_exact(IMG_HEIGHT as u32, IMG_WIDTH as u32, FilterType::Nearest) + .to_rgb(); + println!("resized image dimensions: {:?}", img.dimensions()); + let mut pixels: Vec = vec![]; + for pixel in img.pixels() { + let tmp = pixel.data; + // normalize the RGB channels using mean, std of imagenet1k + let tmp = [ + (tmp[0] as f32 - 123.0) / 58.395, // R + (tmp[1] as f32 - 117.0) / 57.12, // G + (tmp[2] as f32 - 104.0) / 57.375, // B + ]; + for e in &tmp { + pixels.push(*e); + } + } + + // (H,W,C) -> (C,H,W) + let arr = Array::from_shape_vec((IMG_HEIGHT, IMG_WIDTH, 3), pixels).unwrap(); + let arr = arr.permuted_axes([2, 0, 1]); + let arr = Array::from_iter(arr.into_iter().map(|&v| v)); + + return Tensor::from(arr); +} + +fn execute(wasm_backend_file: String, input_data: Tensor) -> Result { + let store = Store::default(); + + // First set up our linker which is going to be linking modules together. We + // want our linker to have wasi available, so we set that up here as well. + let mut linker = Linker::new(&store); + // Create an instance of `Wasi` which contains a `WasiCtx`. Note that + // `WasiCtx` provides a number of ways to configure what the target program + // will have access to. + let wasi = Wasi::new(&store, WasiCtx::new(env::args())?); + wasi.add_to_linker(&mut linker)?; + + let module = Module::from_file(store.engine(), &wasm_backend_file)?; + let instance = linker.instantiate(&module)?; + let memory = instance + .get_memory("memory") + .ok_or(anyhow::format_err!("failed to find `memory` export"))?; + + // Specify the wasm address to access the wasm memory. + let wasm_addr = memory.data_size(); + // Serialize the data into a JSON string. + let in_data = serde_json::to_vec(&input_data)?; + let in_size = in_data.len(); + // Grow up memory size according to in_size to avoid memory leak. + memory.grow((in_size >> 16) as u32 + 1)?; + + // Insert the input data into wasm memory. + for i in 0..in_size { + unsafe { + memory.data_unchecked_mut()[wasm_addr + i] = *in_data.get(i).unwrap(); + } + } + + // Invoke `run` export. + let run = instance + .get_func("run") + .ok_or(anyhow::format_err!("failed to find `run` function export!"))? + .get2::()?; + + let out_size = run(wasm_addr as i32, in_size as i32)?; + if out_size == 0 { + panic!("graph run failed!"); + } + + let out_data = unsafe { &memory.data_unchecked()[wasm_addr..][..out_size as usize] }; + let out_vec: Tensor = serde_json::from_slice(out_data).unwrap(); + Ok(out_vec) +} + +fn output_assert(out_tensor: Tensor) { + let output = out_tensor.to_vec::(); + + // Find the maximum entry in the output and its index. + let mut argmax = -1; + let mut max_prob = 0.; + for i in 0..output.len() { + if output[i] > max_prob { + max_prob = output[i]; + argmax = i as i32; + } + } + + // Create a hash map of (class id, class name) + let mut synset: HashMap = HashMap::new(); + let mut rdr = csv::ReaderBuilder::new().from_reader( + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tools/synset.csv")).as_bytes(), + ); + + for result in rdr.records() { + let record = result.unwrap(); + let id: i32 = record[0].parse().unwrap(); + let cls = record[1].to_string(); + synset.insert(id, cls); + } + + println!( + "input image belongs to the class `{}`", + synset + .get(&argmax) + .expect("cannot find the class id for argmax") + ); +} diff --git a/apps/wasm-dlbackend-tvm/wasm-dlfrontend/src/types.rs b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/src/types.rs similarity index 100% rename from apps/wasm-dlbackend-tvm/wasm-dlfrontend/src/types.rs rename to apps/wasm-graphcompiler-tvm/wasm-graphruntime/src/types.rs diff --git a/apps/wasm-graphcompiler-tvm/wasm-graphruntime/tools/synset.csv b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/tools/synset.csv new file mode 100644 index 0000000000000..d733178f43b4d --- /dev/null +++ b/apps/wasm-graphcompiler-tvm/wasm-graphruntime/tools/synset.csv @@ -0,0 +1,1000 @@ +0,"tench, Tinca tinca" +1,"goldfish, Carassius auratus" +2,"great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias" +3,"tiger shark, Galeocerdo cuvieri" +4,"hammerhead, hammerhead shark" +5,"electric ray, crampfish, numbfish, torpedo" +6,stingray +7,cock +8,hen +9,"ostrich, Struthio camelus" +10,"brambling, Fringilla montifringilla" +11,"goldfinch, Carduelis carduelis" +12,"house finch, linnet, Carpodacus mexicanus" +13,"junco, snowbird" +14,"indigo bunting, indigo finch, indigo bird, Passerina cyanea" +15,"robin, American robin, Turdus migratorius" +16,bulbul +17,jay +18,magpie +19,chickadee +20,"water ouzel, dipper" +21,kite +22,"bald eagle, American eagle, Haliaeetus leucocephalus" +23,vulture +24,"great grey owl, great gray owl, Strix nebulosa" +25,"European fire salamander, Salamandra salamandra" +26,"common newt, Triturus vulgaris" +27,eft +28,"spotted salamander, Ambystoma maculatum" +29,"axolotl, mud puppy, Ambystoma mexicanum" +30,"bullfrog, Rana catesbeiana" +31,"tree frog, tree-frog" +32,"tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui" +33,"loggerhead, loggerhead turtle, Caretta caretta" +34,"leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea" +35,mud turtle +36,terrapin +37,"box turtle, box tortoise" +38,banded gecko +39,"common iguana, iguana, Iguana iguana" +40,"American chameleon, anole, Anolis carolinensis" +41,"whiptail, whiptail lizard" +42,agama +43,"frilled lizard, Chlamydosaurus kingi" +44,alligator lizard +45,"Gila monster, Heloderma suspectum" +46,"green lizard, Lacerta viridis" +47,"African chameleon, Chamaeleo chamaeleon" +48,"Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis" +49,"African crocodile, Nile crocodile, Crocodylus niloticus" +50,"American alligator, Alligator mississipiensis" +51,triceratops +52,"thunder snake, worm snake, Carphophis amoenus" +53,"ringneck snake, ring-necked snake, ring snake" +54,"hognose snake, puff adder, sand viper" +55,"green snake, grass snake" +56,"king snake, kingsnake" +57,"garter snake, grass snake" +58,water snake +59,vine snake +60,"night snake, Hypsiglena torquata" +61,"boa constrictor, Constrictor constrictor" +62,"rock python, rock snake, Python sebae" +63,"Indian cobra, Naja naja" +64,green mamba +65,sea snake +66,"horned viper, cerastes, sand viper, horned asp, Cerastes cornutus" +67,"diamondback, diamondback rattlesnake, Crotalus adamanteus" +68,"sidewinder, horned rattlesnake, Crotalus cerastes" +69,trilobite +70,"harvestman, daddy longlegs, Phalangium opilio" +71,scorpion +72,"black and gold garden spider, Argiope aurantia" +73,"barn spider, Araneus cavaticus" +74,"garden spider, Aranea diademata" +75,"black widow, Latrodectus mactans" +76,tarantula +77,"wolf spider, hunting spider" +78,tick +79,centipede +80,black grouse +81,ptarmigan +82,"ruffed grouse, partridge, Bonasa umbellus" +83,"prairie chicken, prairie grouse, prairie fowl" +84,peacock +85,quail +86,partridge +87,"African grey, African gray, Psittacus erithacus" +88,macaw +89,"sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita" +90,lorikeet +91,coucal +92,bee eater +93,hornbill +94,hummingbird +95,jacamar +96,toucan +97,drake +98,"red-breasted merganser, Mergus serrator" +99,goose +100,"black swan, Cygnus atratus" +101,tusker +102,"echidna, spiny anteater, anteater" +103,"platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus" +104,"wallaby, brush kangaroo" +105,"koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus" +106,wombat +107,jellyfish +108,"sea anemone, anemone" +109,brain coral +110,"flatworm, platyhelminth" +111,"nematode, nematode worm, roundworm" +112,conch +113,snail +114,slug +115,"sea slug, nudibranch" +116,"chiton, coat-of-mail shell, sea cradle, polyplacophore" +117,"chambered nautilus, pearly nautilus, nautilus" +118,"Dungeness crab, Cancer magister" +119,"rock crab, Cancer irroratus" +120,fiddler crab +121,"king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica" +122,"American lobster, Northern lobster, Maine lobster, Homarus americanus" +123,"spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish" +124,"crayfish, crawfish, crawdad, crawdaddy" +125,hermit crab +126,isopod +127,"white stork, Ciconia ciconia" +128,"black stork, Ciconia nigra" +129,spoonbill +130,flamingo +131,"little blue heron, Egretta caerulea" +132,"American egret, great white heron, Egretta albus" +133,bittern +134,crane +135,"limpkin, Aramus pictus" +136,"European gallinule, Porphyrio porphyrio" +137,"American coot, marsh hen, mud hen, water hen, Fulica americana" +138,bustard +139,"ruddy turnstone, Arenaria interpres" +140,"red-backed sandpiper, dunlin, Erolia alpina" +141,"redshank, Tringa totanus" +142,dowitcher +143,"oystercatcher, oyster catcher" +144,pelican +145,"king penguin, Aptenodytes patagonica" +146,"albatross, mollymawk" +147,"grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus" +148,"killer whale, killer, orca, grampus, sea wolf, Orcinus orca" +149,"dugong, Dugong dugon" +150,sea lion +151,Chihuahua +152,Japanese spaniel +153,"Maltese dog, Maltese terrier, Maltese" +154,"Pekinese, Pekingese, Peke" +155,Shih-Tzu +156,Blenheim spaniel +157,papillon +158,toy terrier +159,Rhodesian ridgeback +160,"Afghan hound, Afghan" +161,"basset, basset hound" +162,beagle +163,"bloodhound, sleuthhound" +164,bluetick +165,black-and-tan coonhound +166,"Walker hound, Walker foxhound" +167,English foxhound +168,redbone +169,"borzoi, Russian wolfhound" +170,Irish wolfhound +171,Italian greyhound +172,whippet +173,"Ibizan hound, Ibizan Podenco" +174,"Norwegian elkhound, elkhound" +175,"otterhound, otter hound" +176,"Saluki, gazelle hound" +177,"Scottish deerhound, deerhound" +178,Weimaraner +179,"Staffordshire bullterrier, Staffordshire bull terrier" +180,"American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier" +181,Bedlington terrier +182,Border terrier +183,Kerry blue terrier +184,Irish terrier +185,Norfolk terrier +186,Norwich terrier +187,Yorkshire terrier +188,wire-haired fox terrier +189,Lakeland terrier +190,"Sealyham terrier, Sealyham" +191,"Airedale, Airedale terrier" +192,"cairn, cairn terrier" +193,Australian terrier +194,"Dandie Dinmont, Dandie Dinmont terrier" +195,"Boston bull, Boston terrier" +196,miniature schnauzer +197,giant schnauzer +198,standard schnauzer +199,"Scotch terrier, Scottish terrier, Scottie" +200,"Tibetan terrier, chrysanthemum dog" +201,"silky terrier, Sydney silky" +202,soft-coated wheaten terrier +203,West Highland white terrier +204,"Lhasa, Lhasa apso" +205,flat-coated retriever +206,curly-coated retriever +207,golden retriever +208,Labrador retriever +209,Chesapeake Bay retriever +210,German short-haired pointer +211,"vizsla, Hungarian pointer" +212,English setter +213,"Irish setter, red setter" +214,Gordon setter +215,Brittany spaniel +216,"clumber, clumber spaniel" +217,"English springer, English springer spaniel" +218,Welsh springer spaniel +219,"cocker spaniel, English cocker spaniel, cocker" +220,Sussex spaniel +221,Irish water spaniel +222,kuvasz +223,schipperke +224,groenendael +225,malinois +226,briard +227,kelpie +228,komondor +229,"Old English sheepdog, bobtail" +230,"Shetland sheepdog, Shetland sheep dog, Shetland" +231,collie +232,Border collie +233,"Bouvier des Flandres, Bouviers des Flandres" +234,Rottweiler +235,"German shepherd, German shepherd dog, German police dog, alsatian" +236,"Doberman, Doberman pinscher" +237,miniature pinscher +238,Greater Swiss Mountain dog +239,Bernese mountain dog +240,Appenzeller +241,EntleBucher +242,boxer +243,bull mastiff +244,Tibetan mastiff +245,French bulldog +246,Great Dane +247,"Saint Bernard, St Bernard" +248,"Eskimo dog, husky" +249,"malamute, malemute, Alaskan malamute" +250,Siberian husky +251,"dalmatian, coach dog, carriage dog" +252,"affenpinscher, monkey pinscher, monkey dog" +253,basenji +254,"pug, pug-dog" +255,Leonberg +256,"Newfoundland, Newfoundland dog" +257,Great Pyrenees +258,"Samoyed, Samoyede" +259,Pomeranian +260,"chow, chow chow" +261,keeshond +262,Brabancon griffon +263,"Pembroke, Pembroke Welsh corgi" +264,"Cardigan, Cardigan Welsh corgi" +265,toy poodle +266,miniature poodle +267,standard poodle +268,Mexican hairless +269,"timber wolf, grey wolf, gray wolf, Canis lupus" +270,"white wolf, Arctic wolf, Canis lupus tundrarum" +271,"red wolf, maned wolf, Canis rufus, Canis niger" +272,"coyote, prairie wolf, brush wolf, Canis latrans" +273,"dingo, warrigal, warragal, Canis dingo" +274,"dhole, Cuon alpinus" +275,"African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus" +276,"hyena, hyaena" +277,"red fox, Vulpes vulpes" +278,"kit fox, Vulpes macrotis" +279,"Arctic fox, white fox, Alopex lagopus" +280,"grey fox, gray fox, Urocyon cinereoargenteus" +281,"tabby, tabby cat" +282,tiger cat +283,Persian cat +284,"Siamese cat, Siamese" +285,Egyptian cat +286,"cougar, puma, catamount, mountain lion, painter, panther, Felis concolor" +287,"lynx, catamount" +288,"leopard, Panthera pardus" +289,"snow leopard, ounce, Panthera uncia" +290,"jaguar, panther, Panthera onca, Felis onca" +291,"lion, king of beasts, Panthera leo" +292,"tiger, Panthera tigris" +293,"cheetah, chetah, Acinonyx jubatus" +294,"brown bear, bruin, Ursus arctos" +295,"American black bear, black bear, Ursus americanus, Euarctos americanus" +296,"ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus" +297,"sloth bear, Melursus ursinus, Ursus ursinus" +298,mongoose +299,"meerkat, mierkat" +300,tiger beetle +301,"ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle" +302,"ground beetle, carabid beetle" +303,"long-horned beetle, longicorn, longicorn beetle" +304,"leaf beetle, chrysomelid" +305,dung beetle +306,rhinoceros beetle +307,weevil +308,fly +309,bee +310,"ant, emmet, pismire" +311,"grasshopper, hopper" +312,cricket +313,"walking stick, walkingstick, stick insect" +314,"cockroach, roach" +315,"mantis, mantid" +316,"cicada, cicala" +317,leafhopper +318,"lacewing, lacewing fly" +319,"dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk" +320,damselfly +321,admiral +322,"ringlet, ringlet butterfly" +323,"monarch, monarch butterfly, milkweed butterfly, Danaus plexippus" +324,cabbage butterfly +325,"sulphur butterfly, sulfur butterfly" +326,"lycaenid, lycaenid butterfly" +327,"starfish, sea star" +328,sea urchin +329,"sea cucumber, holothurian" +330,"wood rabbit, cottontail, cottontail rabbit" +331,hare +332,"Angora, Angora rabbit" +333,hamster +334,"porcupine, hedgehog" +335,"fox squirrel, eastern fox squirrel, Sciurus niger" +336,marmot +337,beaver +338,"guinea pig, Cavia cobaya" +339,sorrel +340,zebra +341,"hog, pig, grunter, squealer, Sus scrofa" +342,"wild boar, boar, Sus scrofa" +343,warthog +344,"hippopotamus, hippo, river horse, Hippopotamus amphibius" +345,ox +346,"water buffalo, water ox, Asiatic buffalo, Bubalus bubalis" +347,bison +348,"ram, tup" +349,"bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis" +350,"ibex, Capra ibex" +351,hartebeest +352,"impala, Aepyceros melampus" +353,gazelle +354,"Arabian camel, dromedary, Camelus dromedarius" +355,llama +356,weasel +357,mink +358,"polecat, fitch, foulmart, foumart, Mustela putorius" +359,"black-footed ferret, ferret, Mustela nigripes" +360,otter +361,"skunk, polecat, wood pussy" +362,badger +363,armadillo +364,"three-toed sloth, ai, Bradypus tridactylus" +365,"orangutan, orang, orangutang, Pongo pygmaeus" +366,"gorilla, Gorilla gorilla" +367,"chimpanzee, chimp, Pan troglodytes" +368,"gibbon, Hylobates lar" +369,"siamang, Hylobates syndactylus, Symphalangus syndactylus" +370,"guenon, guenon monkey" +371,"patas, hussar monkey, Erythrocebus patas" +372,baboon +373,macaque +374,langur +375,"colobus, colobus monkey" +376,"proboscis monkey, Nasalis larvatus" +377,marmoset +378,"capuchin, ringtail, Cebus capucinus" +379,"howler monkey, howler" +380,"titi, titi monkey" +381,"spider monkey, Ateles geoffroyi" +382,"squirrel monkey, Saimiri sciureus" +383,"Madagascar cat, ring-tailed lemur, Lemur catta" +384,"indri, indris, Indri indri, Indri brevicaudatus" +385,"Indian elephant, Elephas maximus" +386,"African elephant, Loxodonta africana" +387,"lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens" +388,"giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca" +389,"barracouta, snoek" +390,eel +391,"coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch" +392,"rock beauty, Holocanthus tricolor" +393,anemone fish +394,sturgeon +395,"gar, garfish, garpike, billfish, Lepisosteus osseus" +396,lionfish +397,"puffer, pufferfish, blowfish, globefish" +398,abacus +399,abaya +400,"academic gown, academic robe, judge's robe" +401,"accordion, piano accordion, squeeze box" +402,acoustic guitar +403,"aircraft carrier, carrier, flattop, attack aircraft carrier" +404,airliner +405,"airship, dirigible" +406,altar +407,ambulance +408,"amphibian, amphibious vehicle" +409,analog clock +410,"apiary, bee house" +411,apron +412,"ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin" +413,"assault rifle, assault gun" +414,"backpack, back pack, knapsack, packsack, rucksack, haversack" +415,"bakery, bakeshop, bakehouse" +416,"balance beam, beam" +417,balloon +418,"ballpoint, ballpoint pen, ballpen, Biro" +419,Band Aid +420,banjo +421,"bannister, banister, balustrade, balusters, handrail" +422,barbell +423,barber chair +424,barbershop +425,barn +426,barometer +427,"barrel, cask" +428,"barrow, garden cart, lawn cart, wheelbarrow" +429,baseball +430,basketball +431,bassinet +432,bassoon +433,"bathing cap, swimming cap" +434,bath towel +435,"bathtub, bathing tub, bath, tub" +436,"beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon" +437,"beacon, lighthouse, beacon light, pharos" +438,beaker +439,"bearskin, busby, shako" +440,beer bottle +441,beer glass +442,"bell cote, bell cot" +443,bib +444,"bicycle-built-for-two, tandem bicycle, tandem" +445,"bikini, two-piece" +446,"binder, ring-binder" +447,"binoculars, field glasses, opera glasses" +448,birdhouse +449,boathouse +450,"bobsled, bobsleigh, bob" +451,"bolo tie, bolo, bola tie, bola" +452,"bonnet, poke bonnet" +453,bookcase +454,"bookshop, bookstore, bookstall" +455,bottlecap +456,bow +457,"bow tie, bow-tie, bowtie" +458,"brass, memorial tablet, plaque" +459,"brassiere, bra, bandeau" +460,"breakwater, groin, groyne, mole, bulwark, seawall, jetty" +461,"breastplate, aegis, egis" +462,broom +463,"bucket, pail" +464,buckle +465,bulletproof vest +466,"bullet train, bullet" +467,"butcher shop, meat market" +468,"cab, hack, taxi, taxicab" +469,"caldron, cauldron" +470,"candle, taper, wax light" +471,cannon +472,canoe +473,"can opener, tin opener" +474,cardigan +475,car mirror +476,"carousel, carrousel, merry-go-round, roundabout, whirligig" +477,"carpenter's kit, tool kit" +478,carton +479,car wheel +480,"cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM" +481,cassette +482,cassette player +483,castle +484,catamaran +485,CD player +486,"cello, violoncello" +487,"cellular telephone, cellular phone, cellphone, cell, mobile phone" +488,chain +489,chainlink fence +490,"chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour" +491,"chain saw, chainsaw" +492,chest +493,"chiffonier, commode" +494,"chime, bell, gong" +495,"china cabinet, china closet" +496,Christmas stocking +497,"church, church building" +498,"cinema, movie theater, movie theatre, movie house, picture palace" +499,"cleaver, meat cleaver, chopper" +500,cliff dwelling +501,cloak +502,"clog, geta, patten, sabot" +503,cocktail shaker +504,coffee mug +505,coffeepot +506,"coil, spiral, volute, whorl, helix" +507,combination lock +508,"computer keyboard, keypad" +509,"confectionery, confectionary, candy store" +510,"container ship, containership, container vessel" +511,convertible +512,"corkscrew, bottle screw" +513,"cornet, horn, trumpet, trump" +514,cowboy boot +515,"cowboy hat, ten-gallon hat" +516,cradle +517,crane +518,crash helmet +519,crate +520,"crib, cot" +521,Crock Pot +522,croquet ball +523,crutch +524,cuirass +525,"dam, dike, dyke" +526,desk +527,desktop computer +528,"dial telephone, dial phone" +529,"diaper, nappy, napkin" +530,digital clock +531,digital watch +532,"dining table, board" +533,"dishrag, dishcloth" +534,"dishwasher, dish washer, dishwashing machine" +535,"disk brake, disc brake" +536,"dock, dockage, docking facility" +537,"dogsled, dog sled, dog sleigh" +538,dome +539,"doormat, welcome mat" +540,"drilling platform, offshore rig" +541,"drum, membranophone, tympan" +542,drumstick +543,dumbbell +544,Dutch oven +545,"electric fan, blower" +546,electric guitar +547,electric locomotive +548,entertainment center +549,envelope +550,espresso maker +551,face powder +552,"feather boa, boa" +553,"file, file cabinet, filing cabinet" +554,fireboat +555,"fire engine, fire truck" +556,"fire screen, fireguard" +557,"flagpole, flagstaff" +558,"flute, transverse flute" +559,folding chair +560,football helmet +561,forklift +562,fountain +563,fountain pen +564,four-poster +565,freight car +566,"French horn, horn" +567,"frying pan, frypan, skillet" +568,fur coat +569,"garbage truck, dustcart" +570,"gasmask, respirator, gas helmet" +571,"gas pump, gasoline pump, petrol pump, island dispenser" +572,goblet +573,go-kart +574,golf ball +575,"golfcart, golf cart" +576,gondola +577,"gong, tam-tam" +578,gown +579,"grand piano, grand" +580,"greenhouse, nursery, glasshouse" +581,"grille, radiator grille" +582,"grocery store, grocery, food market, market" +583,guillotine +584,hair slide +585,hair spray +586,half track +587,hammer +588,hamper +589,"hand blower, blow dryer, blow drier, hair dryer, hair drier" +590,"hand-held computer, hand-held microcomputer" +591,"handkerchief, hankie, hanky, hankey" +592,"hard disc, hard disk, fixed disk" +593,"harmonica, mouth organ, harp, mouth harp" +594,harp +595,"harvester, reaper" +596,hatchet +597,holster +598,"home theater, home theatre" +599,honeycomb +600,"hook, claw" +601,"hoopskirt, crinoline" +602,"horizontal bar, high bar" +603,"horse cart, horse-cart" +604,hourglass +605,iPod +606,"iron, smoothing iron" +607,jack-o'-lantern +608,"jean, blue jean, denim" +609,"jeep, landrover" +610,"jersey, T-shirt, tee shirt" +611,jigsaw puzzle +612,"jinrikisha, ricksha, rickshaw" +613,joystick +614,kimono +615,knee pad +616,knot +617,"lab coat, laboratory coat" +618,ladle +619,"lampshade, lamp shade" +620,"laptop, laptop computer" +621,"lawn mower, mower" +622,"lens cap, lens cover" +623,"letter opener, paper knife, paperknife" +624,library +625,lifeboat +626,"lighter, light, igniter, ignitor" +627,"limousine, limo" +628,"liner, ocean liner" +629,"lipstick, lip rouge" +630,Loafer +631,lotion +632,"loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" +633,"loupe, jeweler's loupe" +634,"lumbermill, sawmill" +635,magnetic compass +636,"mailbag, postbag" +637,"mailbox, letter box" +638,maillot +639,"maillot, tank suit" +640,manhole cover +641,maraca +642,"marimba, xylophone" +643,mask +644,matchstick +645,maypole +646,"maze, labyrinth" +647,measuring cup +648,"medicine chest, medicine cabinet" +649,"megalith, megalithic structure" +650,"microphone, mike" +651,"microwave, microwave oven" +652,military uniform +653,milk can +654,minibus +655,"miniskirt, mini" +656,minivan +657,missile +658,mitten +659,mixing bowl +660,"mobile home, manufactured home" +661,Model T +662,modem +663,monastery +664,monitor +665,moped +666,mortar +667,mortarboard +668,mosque +669,mosquito net +670,"motor scooter, scooter" +671,"mountain bike, all-terrain bike, off-roader" +672,mountain tent +673,"mouse, computer mouse" +674,mousetrap +675,moving van +676,muzzle +677,nail +678,neck brace +679,necklace +680,nipple +681,"notebook, notebook computer" +682,obelisk +683,"oboe, hautboy, hautbois" +684,"ocarina, sweet potato" +685,"odometer, hodometer, mileometer, milometer" +686,oil filter +687,"organ, pipe organ" +688,"oscilloscope, scope, cathode-ray oscilloscope, CRO" +689,overskirt +690,oxcart +691,oxygen mask +692,packet +693,"paddle, boat paddle" +694,"paddlewheel, paddle wheel" +695,padlock +696,paintbrush +697,"pajama, pyjama, pj's, jammies" +698,palace +699,"panpipe, pandean pipe, syrinx" +700,paper towel +701,"parachute, chute" +702,"parallel bars, bars" +703,park bench +704,parking meter +705,"passenger car, coach, carriage" +706,"patio, terrace" +707,"pay-phone, pay-station" +708,"pedestal, plinth, footstall" +709,"pencil box, pencil case" +710,pencil sharpener +711,"perfume, essence" +712,Petri dish +713,photocopier +714,"pick, plectrum, plectron" +715,pickelhaube +716,"picket fence, paling" +717,"pickup, pickup truck" +718,pier +719,"piggy bank, penny bank" +720,pill bottle +721,pillow +722,ping-pong ball +723,pinwheel +724,"pirate, pirate ship" +725,"pitcher, ewer" +726,"plane, carpenter's plane, woodworking plane" +727,planetarium +728,plastic bag +729,plate rack +730,"plow, plough" +731,"plunger, plumber's helper" +732,"Polaroid camera, Polaroid Land camera" +733,pole +734,"police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria" +735,poncho +736,"pool table, billiard table, snooker table" +737,"pop bottle, soda bottle" +738,"pot, flowerpot" +739,potter's wheel +740,power drill +741,"prayer rug, prayer mat" +742,printer +743,"prison, prison house" +744,"projectile, missile" +745,projector +746,"puck, hockey puck" +747,"punching bag, punch bag, punching ball, punchball" +748,purse +749,"quill, quill pen" +750,"quilt, comforter, comfort, puff" +751,"racer, race car, racing car" +752,"racket, racquet" +753,radiator +754,"radio, wireless" +755,"radio telescope, radio reflector" +756,rain barrel +757,"recreational vehicle, RV, R.V." +758,reel +759,reflex camera +760,"refrigerator, icebox" +761,"remote control, remote" +762,"restaurant, eating house, eating place, eatery" +763,"revolver, six-gun, six-shooter" +764,rifle +765,"rocking chair, rocker" +766,rotisserie +767,"rubber eraser, rubber, pencil eraser" +768,rugby ball +769,"rule, ruler" +770,running shoe +771,safe +772,safety pin +773,"saltshaker, salt shaker" +774,sandal +775,sarong +776,"sax, saxophone" +777,scabbard +778,"scale, weighing machine" +779,school bus +780,schooner +781,scoreboard +782,"screen, CRT screen" +783,screw +784,screwdriver +785,"seat belt, seatbelt" +786,sewing machine +787,"shield, buckler" +788,"shoe shop, shoe-shop, shoe store" +789,shoji +790,shopping basket +791,shopping cart +792,shovel +793,shower cap +794,shower curtain +795,ski +796,ski mask +797,sleeping bag +798,"slide rule, slipstick" +799,sliding door +800,"slot, one-armed bandit" +801,snorkel +802,snowmobile +803,"snowplow, snowplough" +804,soap dispenser +805,soccer ball +806,sock +807,"solar dish, solar collector, solar furnace" +808,sombrero +809,soup bowl +810,space bar +811,space heater +812,space shuttle +813,spatula +814,speedboat +815,"spider web, spider's web" +816,spindle +817,"sports car, sport car" +818,"spotlight, spot" +819,stage +820,steam locomotive +821,steel arch bridge +822,steel drum +823,stethoscope +824,stole +825,stone wall +826,"stopwatch, stop watch" +827,stove +828,strainer +829,"streetcar, tram, tramcar, trolley, trolley car" +830,stretcher +831,"studio couch, day bed" +832,"stupa, tope" +833,"submarine, pigboat, sub, U-boat" +834,"suit, suit of clothes" +835,sundial +836,sunglass +837,"sunglasses, dark glasses, shades" +838,"sunscreen, sunblock, sun blocker" +839,suspension bridge +840,"swab, swob, mop" +841,sweatshirt +842,"swimming trunks, bathing trunks" +843,swing +844,"switch, electric switch, electrical switch" +845,syringe +846,table lamp +847,"tank, army tank, armored combat vehicle, armoured combat vehicle" +848,tape player +849,teapot +850,"teddy, teddy bear" +851,"television, television system" +852,tennis ball +853,"thatch, thatched roof" +854,"theater curtain, theatre curtain" +855,thimble +856,"thresher, thrasher, threshing machine" +857,throne +858,tile roof +859,toaster +860,"tobacco shop, tobacconist shop, tobacconist" +861,toilet seat +862,torch +863,totem pole +864,"tow truck, tow car, wrecker" +865,toyshop +866,tractor +867,"trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi" +868,tray +869,trench coat +870,"tricycle, trike, velocipede" +871,trimaran +872,tripod +873,triumphal arch +874,"trolleybus, trolley coach, trackless trolley" +875,trombone +876,"tub, vat" +877,turnstile +878,typewriter keyboard +879,umbrella +880,"unicycle, monocycle" +881,"upright, upright piano" +882,"vacuum, vacuum cleaner" +883,vase +884,vault +885,velvet +886,vending machine +887,vestment +888,viaduct +889,"violin, fiddle" +890,volleyball +891,waffle iron +892,wall clock +893,"wallet, billfold, notecase, pocketbook" +894,"wardrobe, closet, press" +895,"warplane, military plane" +896,"washbasin, handbasin, washbowl, lavabo, wash-hand basin" +897,"washer, automatic washer, washing machine" +898,water bottle +899,water jug +900,water tower +901,whiskey jug +902,whistle +903,wig +904,window screen +905,window shade +906,Windsor tie +907,wine bottle +908,wing +909,wok +910,wooden spoon +911,"wool, woolen, woollen" +912,"worm fence, snake fence, snake-rail fence, Virginia fence" +913,wreck +914,yawl +915,yurt +916,"web site, website, internet site, site" +917,comic book +918,"crossword puzzle, crossword" +919,street sign +920,"traffic light, traffic signal, stoplight" +921,"book jacket, dust cover, dust jacket, dust wrapper" +922,menu +923,plate +924,guacamole +925,consomme +926,"hot pot, hotpot" +927,trifle +928,"ice cream, icecream" +929,"ice lolly, lolly, lollipop, popsicle" +930,French loaf +931,"bagel, beigel" +932,pretzel +933,cheeseburger +934,"hotdog, hot dog, red hot" +935,mashed potato +936,head cabbage +937,broccoli +938,cauliflower +939,"zucchini, courgette" +940,spaghetti squash +941,acorn squash +942,butternut squash +943,"cucumber, cuke" +944,"artichoke, globe artichoke" +945,bell pepper +946,cardoon +947,mushroom +948,Granny Smith +949,strawberry +950,orange +951,lemon +952,fig +953,"pineapple, ananas" +954,banana +955,"jackfruit, jak, jack" +956,custard apple +957,pomegranate +958,hay +959,carbonara +960,"chocolate sauce, chocolate syrup" +961,dough +962,"meat loaf, meatloaf" +963,"pizza, pizza pie" +964,potpie +965,burrito +966,red wine +967,espresso +968,cup +969,eggnog +970,alp +971,bubble +972,"cliff, drop, drop-off" +973,coral reef +974,geyser +975,"lakeside, lakeshore" +976,"promontory, headland, head, foreland" +977,"sandbar, sand bar" +978,"seashore, coast, seacoast, sea-coast" +979,"valley, vale" +980,volcano +981,"ballplayer, baseball player" +982,"groom, bridegroom" +983,scuba diver +984,rapeseed +985,daisy +986,"yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum" +987,corn +988,acorn +989,"hip, rose hip, rosehip" +990,"buckeye, horse chestnut, conker" +991,coral fungus +992,agaric +993,gyromitra +994,"stinkhorn, carrion fungus" +995,earthstar +996,"hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa" +997,bolete +998,"ear, spike, capitulum" +999,"toilet tissue, toilet paper, bathroom tissue" diff --git a/rust/runtime/src/array.rs b/rust/runtime/src/array.rs index c38b3ff8e527f..896812a02bbbb 100644 --- a/rust/runtime/src/array.rs +++ b/rust/runtime/src/array.rs @@ -279,7 +279,7 @@ impl<'a> Tensor<'a> { } } - pub(crate) fn as_dltensor(&self, flatten: bool) -> DLTensor { + pub fn as_dltensor(&self, flatten: bool) -> DLTensor { assert!(!flatten || self.is_contiguous()); DLTensor { data: unsafe { self.data.as_mut_ptr().offset(self.byte_offset) } as *mut c_void,