You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by GitBox <gi...@apache.org> on 2020/07/20 03:06:40 UTC

[GitHub] [incubator-tvm] nhynes commented on a change in pull request #5892: Add TVM application extension with WASM runtime

nhynes commented on a change in pull request #5892:
URL: https://github.com/apache/incubator-tvm/pull/5892#discussion_r457000729



##########
File path: apps/wasm-standalone/wasm-graph/src/lib.rs
##########
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate lazy_static;
+#[macro_use]
+extern crate serde_derive;
+extern crate ndarray;
+extern crate tvm_runtime;

Review comment:
       `edition = 2018` doesn't require `extern crate` declarations.

##########
File path: apps/wasm-standalone/wasm-graph/src/lib.rs
##########
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate lazy_static;
+#[macro_use]
+extern crate serde_derive;
+extern crate ndarray;
+extern crate tvm_runtime;
+
+mod types;
+use types::Tensor;

Review comment:
       I usually format imports as 
   
   ```rust
   #[macro_use]
   extern crate extern_crate;
   
   mod internal_module;
   
   use std::any;
   
   use extern_crate::module;
   
   use internal_module::inner;
   ```
   this matches how google does python imports

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+const IMG_HEIGHT: usize = 224;
+const IMG_WIDTH: usize = 224;
+
+fn print_usage(program: &str, opts: Options) {

Review comment:
       I get the desire for minimalism, but [clap](https://crates.io/crates/clap) (especially the `clap_app` macro) would really make the code a lot cleaner.

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+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<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optopt(
+        "g",
+        "wasm-graph-file",
+        "set the path to wasm graph file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "i",
+        "input-data-file",
+        "set the path to input image file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "l",
+        "label-class-file",
+        "set the path to label class 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_graph_file: String = match matches.opt_str("g") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let input_data_file: String = match matches.opt_str("i") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let label_class_file: String = match matches.opt_str("l") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let img = image::open(input_data_file).unwrap();
+    let input = data_preprocess(img);
+
+    let output: Tensor = match runtime::execute(wasm_graph_file, input) {
+        Ok(m) => m,
+        Err(f) => panic!(f.to_string()),
+    };
+    output_assert(output, label_class_file);
+}
+
+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<f32> = vec![];

Review comment:
       `Vec::with_capacity(img.len())` you already know the size. this is a useless micro-optimization, though 😉 

##########
File path: apps/wasm-standalone/wasm-graph/Cargo.toml
##########
@@ -0,0 +1,43 @@
+# 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.
+
+[package]
+name = "wasm-graph"
+version = "0.1.0"
+authors = ["TVM Contributors"]
+edition = "2018"
+description = "WebAssembly graph to deep learning frameworks using TVM"
+readme = "README.md"
+repository = "https://github.com/apache/incubator-tvm"
+license = "Apache-2.0"
+keywords = ["wasm", "machine learning", "tvm"]
+
+[profile.release]
+lto = true
+opt-level = 's'
+
+[lib]
+crate-type = ['cdylib']
+
+[dependencies]
+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" }
+lazy_static = "1.1.1"

Review comment:
       patch versions are superfluous, as cargo [defaults to carat](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements)

##########
File path: apps/wasm-standalone/wasm-graph/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)
+    target = 'llvm -target=wasm32-unknown-unknown -mattr=+simd128 --system-lib'

Review comment:
       cool simd!

##########
File path: apps/wasm-standalone/wasm-graph/src/types.rs
##########
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+use std::{
+    any::TypeId,
+    convert::From,
+    os::raw::{c_int, c_void},
+    slice,
+};
+pub use tvm_common::ffi::DLTensor;
+use tvm_common::ffi::{
+    DLContext, DLDataType, DLDataTypeCode_kDLFloat, DLDataTypeCode_kDLInt, DLDeviceType_kDLCPU,
+};
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+pub enum DataType {
+    FP32,
+    INT32,
+    INT8,
+}
+
+impl DataType {
+    pub fn as_dldtype(&self) -> DLDataType {
+        match self {
+            DataType::INT32 => DLDataType {
+                code: DLDataTypeCode_kDLInt as u8,
+                bits: 32u8,
+                lanes: 1u16,
+            },
+            DataType::INT8 => DLDataType {
+                code: DLDataTypeCode_kDLInt as u8,
+                bits: 8u8,
+                lanes: 1u16,
+            },
+            DataType::FP32 => DLDataType {
+                code: DLDataTypeCode_kDLFloat as u8,
+                bits: 32u8,
+                lanes: 1u16,
+            },
+        }
+    }
+
+    /// Returns whether this `DataType` represents primitive type `T`.
+    pub fn is_type<T: 'static>(&self) -> bool {
+        let typ = TypeId::of::<T>();
+        typ == TypeId::of::<i32>() || typ == TypeId::of::<i8>() || typ == TypeId::of::<f32>()
+    }
+}
+
+impl From<DLDataType> for DataType {
+    fn from(dl_dtype: DLDataType) -> Self {
+        if dl_dtype.code == DLDataTypeCode_kDLInt as u8 && dl_dtype.bits == 32u8 {
+            DataType::INT32
+        } else if dl_dtype.code == DLDataTypeCode_kDLInt as u8 && dl_dtype.bits == 8u8 {
+            DataType::INT8
+        } else if dl_dtype.code == DLDataTypeCode_kDLFloat as u8 && dl_dtype.bits == 32u8 {
+            DataType::FP32
+        } else {
+            DataType::FP32
+        }
+    }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Tensor {

Review comment:
       yeah this is unfortunate. would you mind making the patch to the actual tensor? if it's something you find yourself needing, others would likely, too.

##########
File path: apps/wasm-standalone/wasm-graph/src/utils.rs
##########
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+use super::types::*;
+use serde_json;
+use std::ptr;
+
+pub fn load_input(in_addr: i32, in_size: usize) -> Tensor {

Review comment:
       a function that takes a raw pointer (albeit into linear memory) should likely be an `unsafe fn`

##########
File path: apps/wasm-standalone/wasm-graph/src/types.rs
##########
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+use std::{
+    any::TypeId,
+    convert::From,

Review comment:
       ```suggestion
   ```
   `From` is a default import

##########
File path: apps/wasm-standalone/wasm-graph/build.rs
##########
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+use std::path::PathBuf;
+
+fn main() {
+    let mut out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    out_dir.push("lib");

Review comment:
       ```suggestion
   	let out_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/lib");
   ```
   no need for runtime path construction

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+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<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optopt(
+        "g",
+        "wasm-graph-file",
+        "set the path to wasm graph file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "i",
+        "input-data-file",
+        "set the path to input image file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "l",
+        "label-class-file",
+        "set the path to label class 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_graph_file: String = match matches.opt_str("g") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let input_data_file: String = match matches.opt_str("i") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let label_class_file: String = match matches.opt_str("l") {
+        Some(s) => s,
+        None => String::from(""),

Review comment:
       `unwrap_or_default()`

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/runtime.rs
##########
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+use super::types::Tensor;
+use anyhow::Result;
+use serde_json;

Review comment:
       ```suggestion
   ```
   already in the namespace, per 2018 semantics

##########
File path: tests/lint/check_file_type.py
##########
@@ -159,7 +160,7 @@ def copyright_line(line):
     if line.find("Copyright " + "(c)") != -1:
         return True
     if (line.find("Copyright") != -1 and
-        line.find(" by") != -1):
+            line.find(" by") != -1):

Review comment:
       this doesn't conflict with the existing lint rules? hmm

##########
File path: apps/wasm-standalone/wasm-graphruntime/Cargo.toml
##########
@@ -0,0 +1,37 @@
+# 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.
+
+[package]
+name = "wasm-graphruntime"
+version = "0.1.0"
+authors = ["TVM Contributors"]
+edition = "2018"
+description = "WebAssembly graph runtime to deep learning frameworks using wasmtime"
+license = "Apache-2.0"
+keywords = ["wasm", "machine learning", "wasmtime"]
+
+[dependencies]
+wasmtime = "0.16.0"
+wasmtime-wasi = "0.16.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"

Review comment:
       sort these, please

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+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<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optopt(
+        "g",
+        "wasm-graph-file",
+        "set the path to wasm graph file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "i",
+        "input-data-file",
+        "set the path to input image file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "l",
+        "label-class-file",
+        "set the path to label class 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_graph_file: String = match matches.opt_str("g") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let input_data_file: String = match matches.opt_str("i") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let label_class_file: String = match matches.opt_str("l") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let img = image::open(input_data_file).unwrap();
+    let input = data_preprocess(img);
+
+    let output: Tensor = match runtime::execute(wasm_graph_file, input) {

Review comment:
       `map_err(|e| panic!("{}", e))`
   
   Have you tried running `cargo clippy` on this codebase? I think it'll give you some good suggestions

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+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<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optopt(
+        "g",
+        "wasm-graph-file",
+        "set the path to wasm graph file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "i",
+        "input-data-file",
+        "set the path to input image file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "l",
+        "label-class-file",
+        "set the path to label class 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_graph_file: String = match matches.opt_str("g") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let input_data_file: String = match matches.opt_str("i") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let label_class_file: String = match matches.opt_str("l") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let img = image::open(input_data_file).unwrap();
+    let input = data_preprocess(img);
+
+    let output: Tensor = match runtime::execute(wasm_graph_file, input) {
+        Ok(m) => m,
+        Err(f) => panic!(f.to_string()),
+    };
+    output_assert(output, label_class_file);
+}
+
+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<f32> = 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 output_assert(out_tensor: Tensor, label_class_file: String) {
+    let output = out_tensor.to_vec::<f32>();
+
+    // 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() {

Review comment:
       alternatively, `output.iter().enumerate().max_by_key(|(i, v)| ...)`, if you're feeling functional

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/runtime.rs
##########
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+use super::types::Tensor;
+use anyhow::Result;
+use serde_json;
+use wasmtime::*;
+use wasmtime_wasi::{Wasi, WasiCtx};
+
+pub fn execute(wasm_backend_file: String, input_data: Tensor) -> Result<Tensor> {
+    let engine = Engine::new(Config::new().wasm_simd(true));
+    let store = Store::new(&engine);
+
+    // 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, &wasm_backend_file)?;
+    let instance = linker.instantiate(&module)?;
+    let memory = instance
+        .get_memory("memory")
+        .ok_or(anyhow::format_err!("failed to find `memory` export"))?;

Review comment:
       why not add `format_err` to the `use anyhow`?

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/main.rs
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod types;
+use types::Tensor;
+mod runtime;
+
+use getopts::Options;
+use image::{FilterType, GenericImageView};
+use ndarray::Array;
+use std::{collections::HashMap, env, fs::File, io::BufReader};
+
+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<String> = env::args().collect();
+    let program = args[0].clone();
+
+    let mut opts = Options::new();
+    opts.optopt(
+        "g",
+        "wasm-graph-file",
+        "set the path to wasm graph file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "i",
+        "input-data-file",
+        "set the path to input image file",
+        "FILE_PATH",
+    );
+    opts.optopt(
+        "l",
+        "label-class-file",
+        "set the path to label class 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_graph_file: String = match matches.opt_str("g") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let input_data_file: String = match matches.opt_str("i") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let label_class_file: String = match matches.opt_str("l") {
+        Some(s) => s,
+        None => String::from(""),
+    };
+    let img = image::open(input_data_file).unwrap();
+    let input = data_preprocess(img);
+
+    let output: Tensor = match runtime::execute(wasm_graph_file, input) {
+        Ok(m) => m,
+        Err(f) => panic!(f.to_string()),
+    };
+    output_assert(output, label_class_file);
+}
+
+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<f32> = 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 output_assert(out_tensor: Tensor, label_class_file: String) {
+    let output = out_tensor.to_vec::<f32>();
+
+    // 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<i32, String> = HashMap::new();

Review comment:
       strictly speaking, the map is dense in the key set, so you could use a `Vec` if you had the length beforehand. Minor memory saving. I wouldn't bother with it.

##########
File path: apps/wasm-standalone/wasm-graphruntime/src/runtime.rs
##########
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+use super::types::Tensor;
+use anyhow::Result;
+use serde_json;
+use wasmtime::*;
+use wasmtime_wasi::{Wasi, WasiCtx};
+
+pub fn execute(wasm_backend_file: String, input_data: Tensor) -> Result<Tensor> {
+    let engine = Engine::new(Config::new().wasm_simd(true));
+    let store = Store::new(&engine);
+
+    // 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, &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!"))?

Review comment:
       clippy will ask you to make this an `ok_or_else`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org