Integrate WASI-NN into WAMR (#1521)
Initial integration of WASI-NN based on #1225: - Implement the library core/iwasm/libraries/wasi-nn - Support TensorFlow, CPU, F32 at the first stage - Add cmake variable `-DWAMR_BUILD_WASI_NN` - Add test case based on Docker image and update document Refer to #1573
This commit is contained in:
178
core/iwasm/libraries/wasi-nn/test/CMakeLists.txt
Normal file
178
core/iwasm/libraries/wasi-nn/test/CMakeLists.txt
Normal file
@ -0,0 +1,178 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
cmake_minimum_required (VERSION 2.9)
|
||||
|
||||
project (iwasm)
|
||||
|
||||
set (CMAKE_VERBOSE_MAKEFILE OFF)
|
||||
# Reset default linker flags
|
||||
set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
|
||||
set (CMAKE_C_STANDARD 99)
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_PLATFORM)
|
||||
set (WAMR_BUILD_PLATFORM "linux")
|
||||
endif ()
|
||||
|
||||
# Set WAMR_BUILD_TARGET, currently values supported:
|
||||
# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]",
|
||||
# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]"
|
||||
if (NOT DEFINED WAMR_BUILD_TARGET)
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
|
||||
set (WAMR_BUILD_TARGET "AARCH64")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
|
||||
set (WAMR_BUILD_TARGET "RISCV64")
|
||||
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
# Build as X86_64 by default in 64-bit platform
|
||||
set (WAMR_BUILD_TARGET "X86_64")
|
||||
elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
# Build as X86_32 by default in 32-bit platform
|
||||
set (WAMR_BUILD_TARGET "X86_32")
|
||||
else ()
|
||||
message(SEND_ERROR "Unsupported build target platform!")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_INTERP)
|
||||
# Enable Interpreter by default
|
||||
set (WAMR_BUILD_INTERP 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_AOT)
|
||||
# Enable AOT by default.
|
||||
set (WAMR_BUILD_AOT 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_JIT)
|
||||
# Disable JIT by default.
|
||||
set (WAMR_BUILD_JIT 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_FAST_JIT)
|
||||
# Disable Fast JIT by default
|
||||
set (WAMR_BUILD_FAST_JIT 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN)
|
||||
# Enable libc builtin support by default
|
||||
set (WAMR_BUILD_LIBC_BUILTIN 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_LIBC_WASI)
|
||||
# Enable libc wasi support by default
|
||||
set (WAMR_BUILD_LIBC_WASI 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_FAST_INTERP)
|
||||
# Enable fast interpreter
|
||||
set (WAMR_BUILD_FAST_INTERP 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_MULTI_MODULE)
|
||||
# Disable multiple modules by default
|
||||
set (WAMR_BUILD_MULTI_MODULE 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD)
|
||||
# Disable pthread library by default
|
||||
set (WAMR_BUILD_LIB_PTHREAD 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_MINI_LOADER)
|
||||
# Disable wasm mini loader by default
|
||||
set (WAMR_BUILD_MINI_LOADER 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_SIMD)
|
||||
# Enable SIMD by default
|
||||
set (WAMR_BUILD_SIMD 1)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_REF_TYPES)
|
||||
# Disable reference types by default
|
||||
set (WAMR_BUILD_REF_TYPES 0)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP)
|
||||
# Disable Debug feature by default
|
||||
set (WAMR_BUILD_DEBUG_INTERP 0)
|
||||
endif ()
|
||||
|
||||
if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
|
||||
set (WAMR_BUILD_FAST_INTERP 0)
|
||||
set (WAMR_BUILD_MINI_LOADER 0)
|
||||
set (WAMR_BUILD_SIMD 0)
|
||||
endif ()
|
||||
|
||||
if (COLLECT_CODE_COVERAGE EQUAL 1)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
endif ()
|
||||
|
||||
set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../..)
|
||||
|
||||
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
|
||||
add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
|
||||
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE")
|
||||
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wshadow")
|
||||
# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wsign-conversion")
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wno-unused")
|
||||
|
||||
if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64")
|
||||
if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mindirect-branch-register")
|
||||
# UNDEFINED BEHAVIOR, refer to https://en.cppreference.com/w/cpp/language/ub
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT WAMR_BUILD_JIT EQUAL 1)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined \
|
||||
-fno-sanitize=bounds,bounds-strict,alignment \
|
||||
-fno-sanitize-recover")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined \
|
||||
-fno-sanitize=bounds,bounds-strict,alignment \
|
||||
-fno-sanitize-recover")
|
||||
endif()
|
||||
else ()
|
||||
# UNDEFINED BEHAVIOR, refer to https://en.cppreference.com/w/cpp/language/ub
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT WAMR_BUILD_JIT EQUAL 1)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined \
|
||||
-fno-sanitize=bounds,alignment \
|
||||
-fno-sanitize-recover")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined \
|
||||
-fno-sanitize=bounds,alignment \
|
||||
-fno-sanitize-recover")
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# The following flags are to enhance security, but it may impact performance,
|
||||
# we disable them by default.
|
||||
#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64")
|
||||
# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2")
|
||||
#endif ()
|
||||
#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4")
|
||||
#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now")
|
||||
|
||||
include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
|
||||
|
||||
add_executable (iwasm ${WAMR_ROOT_DIR}/product-mini/platforms/${WAMR_BUILD_PLATFORM}/main.c ${UNCOMMON_SHARED_SOURCE})
|
||||
|
||||
install (TARGETS iwasm DESTINATION bin)
|
||||
|
||||
target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} ${TENSORFLOW_LIB} -lm -ldl -lpthread)
|
||||
|
||||
add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE})
|
||||
|
||||
install (TARGETS libiwasm DESTINATION lib)
|
||||
|
||||
set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm)
|
||||
|
||||
target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread)
|
||||
32
core/iwasm/libraries/wasi-nn/test/Dockerfile
Normal file
32
core/iwasm/libraries/wasi-nn/test/Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
cmake build-essential git wget python3.10 python3-pip
|
||||
|
||||
RUN wget -q https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-14/wasi-sdk-14.0-linux.tar.gz && \
|
||||
tar xf wasi-sdk-*-linux.tar.gz -C /opt && rm -f wasi-sdk-*-linux.tar.gz && \
|
||||
mv /opt/wasi-sdk-14.0 /opt/wasi-sdk
|
||||
|
||||
WORKDIR /home/wamr
|
||||
|
||||
COPY core core
|
||||
COPY build-scripts build-scripts
|
||||
COPY product-mini product-mini
|
||||
|
||||
RUN pip3 install -r core/iwasm/libraries/wasi-nn/test/requirements.txt
|
||||
|
||||
WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test/build
|
||||
|
||||
RUN cmake -DWAMR_BUILD_WASI_NN=1 ..
|
||||
RUN make -j $(grep -c ^processor /proc/cpuinfo)
|
||||
|
||||
WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test
|
||||
|
||||
RUN ./build.sh
|
||||
|
||||
ENTRYPOINT [ "./build/iwasm", "--dir=.", "test_tensorflow.wasm" ]
|
||||
20
core/iwasm/libraries/wasi-nn/test/build.sh
Executable file
20
core/iwasm/libraries/wasi-nn/test/build.sh
Executable file
@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
# WASM application that uses WASI-NN
|
||||
|
||||
/opt/wasi-sdk/bin/clang \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--strip-all,--no-entry \
|
||||
--sysroot=/opt/wasi-sdk/share/wasi-sysroot \
|
||||
-I/home/wamr/core/iwasm/libraries/wasi-nn \
|
||||
-o test_tensorflow.wasm test_tensorflow.c
|
||||
|
||||
# TFLite models to use in the tests
|
||||
|
||||
cd models
|
||||
python3 average.py
|
||||
python3 max.py
|
||||
python3 mult_dimension.py
|
||||
python3 mult_outputs.py
|
||||
python3 sum.py
|
||||
16
core/iwasm/libraries/wasi-nn/test/models/average.py
Executable file
16
core/iwasm/libraries/wasi-nn/test/models/average.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
from utils import save_model
|
||||
|
||||
model = tf.keras.Sequential([
|
||||
tf.keras.layers.InputLayer(input_shape=[5, 5, 1]),
|
||||
tf.keras.layers.AveragePooling2D(
|
||||
pool_size=(5, 5), strides=None, padding="valid", data_format=None)
|
||||
|
||||
])
|
||||
|
||||
# Export model to tflite
|
||||
|
||||
save_model(model, "average.tflite")
|
||||
17
core/iwasm/libraries/wasi-nn/test/models/max.py
Executable file
17
core/iwasm/libraries/wasi-nn/test/models/max.py
Executable file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
from utils import save_model
|
||||
|
||||
model = tf.keras.Sequential([
|
||||
tf.keras.layers.InputLayer(input_shape=[5, 5, 1]),
|
||||
tf.keras.layers.MaxPooling2D(
|
||||
pool_size=(5, 5), strides=None, padding="valid", data_format=None)
|
||||
|
||||
])
|
||||
|
||||
# Export model to tflite
|
||||
|
||||
save_model(model, "max.tflite")
|
||||
15
core/iwasm/libraries/wasi-nn/test/models/mult_dimension.py
Normal file
15
core/iwasm/libraries/wasi-nn/test/models/mult_dimension.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
from utils import save_model
|
||||
|
||||
model = tf.keras.Sequential([
|
||||
tf.keras.layers.InputLayer(input_shape=[3, 3, 1]),
|
||||
tf.keras.layers.Conv2D(1, (1, 1), kernel_initializer=tf.keras.initializers.Constant(
|
||||
value=1), bias_initializer='zeros'
|
||||
)
|
||||
])
|
||||
# Export model to tflite
|
||||
|
||||
save_model(model, "mult_dim.tflite")
|
||||
33
core/iwasm/libraries/wasi-nn/test/models/mult_outputs.py
Executable file
33
core/iwasm/libraries/wasi-nn/test/models/mult_outputs.py
Executable file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
import numpy as np
|
||||
from keras.layers import AveragePooling2D, Conv2D
|
||||
|
||||
from tensorflow.keras import Input, Model
|
||||
|
||||
from utils import save_model
|
||||
|
||||
|
||||
inputs = Input(shape=(4, 4, 1))
|
||||
|
||||
output1 = Conv2D(1, (4, 1), kernel_initializer=tf.keras.initializers.Constant(
|
||||
value=1), bias_initializer='zeros'
|
||||
)(inputs)
|
||||
output2 = AveragePooling2D(pool_size=(
|
||||
4, 1), strides=None, padding="valid", data_format=None)(inputs)
|
||||
|
||||
model = Model(inputs=inputs, outputs=[output1, output2])
|
||||
|
||||
inp = np.arange(16).reshape((1, 4, 4, 1))
|
||||
|
||||
print(inp)
|
||||
|
||||
res = model.predict(inp)
|
||||
|
||||
print(res)
|
||||
print(res[0].shape)
|
||||
print(res[1].shape)
|
||||
|
||||
save_model(model, "mult_out.tflite")
|
||||
17
core/iwasm/libraries/wasi-nn/test/models/sum.py
Executable file
17
core/iwasm/libraries/wasi-nn/test/models/sum.py
Executable file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
from utils import save_model
|
||||
|
||||
model = tf.keras.Sequential([
|
||||
tf.keras.layers.InputLayer(input_shape=[5, 5, 1]),
|
||||
tf.keras.layers.Conv2D(1, (5, 5), kernel_initializer=tf.keras.initializers.Constant(
|
||||
value=1), bias_initializer='zeros'
|
||||
)
|
||||
])
|
||||
|
||||
# Export model to tflite
|
||||
|
||||
save_model(model, "sum.tflite")
|
||||
13
core/iwasm/libraries/wasi-nn/test/models/utils.py
Normal file
13
core/iwasm/libraries/wasi-nn/test/models/utils.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
import tensorflow as tf
|
||||
import pathlib
|
||||
|
||||
|
||||
def save_model(model, filename):
|
||||
converter = tf.lite.TFLiteConverter.from_keras_model(model)
|
||||
tflite_model = converter.convert()
|
||||
tflite_models_dir = pathlib.Path("./")
|
||||
tflite_model_file = tflite_models_dir/filename
|
||||
tflite_model_file.write_bytes(tflite_model)
|
||||
1
core/iwasm/libraries/wasi-nn/test/requirements.txt
Normal file
1
core/iwasm/libraries/wasi-nn/test/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
tensorflow==2.10.0
|
||||
301
core/iwasm/libraries/wasi-nn/test/test_tensorflow.c
Executable file
301
core/iwasm/libraries/wasi-nn/test/test_tensorflow.c
Executable file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include "wasi_nn.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define MAX_MODEL_SIZE 85000000
|
||||
#define MAX_OUTPUT_TENSOR_SIZE 200
|
||||
#define INPUT_TENSOR_DIMS 4
|
||||
#define EPSILON 1e-8
|
||||
|
||||
typedef struct {
|
||||
float *input_tensor;
|
||||
uint32_t *dim;
|
||||
uint32_t elements;
|
||||
} input_info;
|
||||
|
||||
// WASI-NN wrappers
|
||||
|
||||
error
|
||||
wasm_load(char *model_name, graph *graph)
|
||||
{
|
||||
FILE *pFile = fopen(model_name, "r");
|
||||
if (pFile == NULL)
|
||||
return invalid_argument;
|
||||
|
||||
uint8_t *buffer;
|
||||
size_t result;
|
||||
|
||||
// allocate memory to contain the whole file:
|
||||
buffer = (uint8_t *)malloc(sizeof(uint8_t) * MAX_MODEL_SIZE);
|
||||
if (buffer == NULL) {
|
||||
fclose(pFile);
|
||||
return missing_memory;
|
||||
}
|
||||
|
||||
result = fread(buffer, 1, MAX_MODEL_SIZE, pFile);
|
||||
if (result <= 0) {
|
||||
fclose(pFile);
|
||||
free(buffer);
|
||||
return missing_memory;
|
||||
}
|
||||
|
||||
graph_builder_array arr;
|
||||
|
||||
arr.size = 1;
|
||||
arr.buf = (graph_builder *)malloc(sizeof(graph_builder));
|
||||
if (arr.buf == NULL) {
|
||||
fclose(pFile);
|
||||
free(buffer);
|
||||
return missing_memory;
|
||||
}
|
||||
|
||||
arr.buf[0].size = result;
|
||||
arr.buf[0].buf = buffer;
|
||||
|
||||
error res = load(&arr, tensorflow, cpu, graph);
|
||||
|
||||
fclose(pFile);
|
||||
free(buffer);
|
||||
free(arr.buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
error
|
||||
wasm_init_execution_context(graph graph, graph_execution_context *ctx)
|
||||
{
|
||||
return init_execution_context(graph, ctx);
|
||||
}
|
||||
|
||||
error
|
||||
wasm_input(graph_execution_context ctx, float *input_tensor, uint32_t *dim)
|
||||
{
|
||||
tensor_dimensions dims;
|
||||
dims.size = INPUT_TENSOR_DIMS;
|
||||
dims.buf = (uint32_t *)malloc(dims.size * sizeof(uint32_t));
|
||||
if (dims.buf == NULL)
|
||||
return missing_memory;
|
||||
|
||||
tensor tensor;
|
||||
tensor.dimensions = &dims;
|
||||
for (int i = 0; i < tensor.dimensions->size; ++i)
|
||||
tensor.dimensions->buf[i] = dim[i];
|
||||
tensor.type = fp32;
|
||||
tensor.data = (uint8_t *)input_tensor;
|
||||
error err = set_input(ctx, 0, &tensor);
|
||||
|
||||
free(dims.buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
error
|
||||
wasm_compute(graph_execution_context ctx)
|
||||
{
|
||||
return compute(ctx);
|
||||
}
|
||||
|
||||
error
|
||||
wasm_get_output(graph_execution_context ctx, uint32_t index, float *out_tensor,
|
||||
uint32_t *out_size)
|
||||
{
|
||||
return get_output(ctx, index, (uint8_t *)out_tensor, out_size);
|
||||
}
|
||||
|
||||
// Inference
|
||||
|
||||
float *
|
||||
run_inference(float *input, uint32_t *input_size, uint32_t *output_size,
|
||||
char *model_name, uint32_t num_output_tensors)
|
||||
{
|
||||
graph graph;
|
||||
if (wasm_load(model_name, &graph) != success) {
|
||||
fprintf(stderr, "Error when loading model.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
graph_execution_context ctx;
|
||||
if (wasm_init_execution_context(graph, &ctx) != success) {
|
||||
fprintf(stderr, "Error when initialixing execution context.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (wasm_input(ctx, input, input_size) != success) {
|
||||
fprintf(stderr, "Error when setting input tensor.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (wasm_compute(ctx) != success) {
|
||||
fprintf(stderr, "Error when running inference.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
float *out_tensor = (float *)malloc(sizeof(float) * MAX_OUTPUT_TENSOR_SIZE);
|
||||
if (out_tensor == NULL) {
|
||||
fprintf(stderr, "Error when allocating memory for output tensor.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
for (int i = 0; i < num_output_tensors; ++i) {
|
||||
*output_size = MAX_OUTPUT_TENSOR_SIZE - *output_size;
|
||||
if (wasm_get_output(ctx, i, &out_tensor[offset], output_size)
|
||||
!= success) {
|
||||
fprintf(stderr, "Error when getting input .");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
offset += *output_size;
|
||||
}
|
||||
*output_size = offset;
|
||||
return out_tensor;
|
||||
}
|
||||
|
||||
// UTILS
|
||||
|
||||
input_info
|
||||
create_input(int *dims)
|
||||
{
|
||||
input_info input = { .dim = NULL, .input_tensor = NULL, .elements = 1 };
|
||||
|
||||
input.dim = malloc(INPUT_TENSOR_DIMS * sizeof(uint32_t));
|
||||
if (input.dim)
|
||||
for (int i = 0; i < INPUT_TENSOR_DIMS; ++i) {
|
||||
input.dim[i] = dims[i];
|
||||
input.elements *= dims[i];
|
||||
}
|
||||
|
||||
input.input_tensor = malloc(input.elements * sizeof(float));
|
||||
for (int i = 0; i < input.elements; ++i)
|
||||
input.input_tensor[i] = i;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
// TESTS
|
||||
|
||||
void
|
||||
test_sum()
|
||||
{
|
||||
int dims[] = { 1, 5, 5, 1 };
|
||||
input_info input = create_input(dims);
|
||||
|
||||
uint32_t output_size = 0;
|
||||
float *output = run_inference(input.input_tensor, input.dim, &output_size,
|
||||
"models/sum.tflite", 1);
|
||||
|
||||
assert(output_size == 1);
|
||||
assert(fabs(output[0] - 300.0) < EPSILON);
|
||||
|
||||
free(input.dim);
|
||||
free(input.input_tensor);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void
|
||||
test_max()
|
||||
{
|
||||
int dims[] = { 1, 5, 5, 1 };
|
||||
input_info input = create_input(dims);
|
||||
|
||||
uint32_t output_size = 0;
|
||||
float *output = run_inference(input.input_tensor, input.dim, &output_size,
|
||||
"models/max.tflite", 1);
|
||||
|
||||
assert(output_size == 1);
|
||||
assert(fabs(output[0] - 24.0) < EPSILON);
|
||||
printf("Result: max is %f\n", output[0]);
|
||||
|
||||
free(input.dim);
|
||||
free(input.input_tensor);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void
|
||||
test_average()
|
||||
{
|
||||
int dims[] = { 1, 5, 5, 1 };
|
||||
input_info input = create_input(dims);
|
||||
|
||||
uint32_t output_size = 0;
|
||||
float *output = run_inference(input.input_tensor, input.dim, &output_size,
|
||||
"models/average.tflite", 1);
|
||||
|
||||
assert(output_size == 1);
|
||||
assert(fabs(output[0] - 12.0) < EPSILON);
|
||||
printf("Result: average is %f\n", output[0]);
|
||||
|
||||
free(input.dim);
|
||||
free(input.input_tensor);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void
|
||||
test_mult_dimensions()
|
||||
{
|
||||
int dims[] = { 1, 3, 3, 1 };
|
||||
input_info input = create_input(dims);
|
||||
|
||||
uint32_t output_size = 0;
|
||||
float *output = run_inference(input.input_tensor, input.dim, &output_size,
|
||||
"models/mult_dim.tflite", 1);
|
||||
|
||||
assert(output_size == 9);
|
||||
for (int i = 0; i < 9; i++)
|
||||
assert(fabs(output[i] - i) < EPSILON);
|
||||
|
||||
free(input.dim);
|
||||
free(input.input_tensor);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void
|
||||
test_mult_outputs()
|
||||
{
|
||||
int dims[] = { 1, 4, 4, 1 };
|
||||
input_info input = create_input(dims);
|
||||
|
||||
uint32_t output_size = 0;
|
||||
float *output = run_inference(input.input_tensor, input.dim, &output_size,
|
||||
"models/mult_out.tflite", 2);
|
||||
|
||||
assert(output_size == 8);
|
||||
// first tensor check
|
||||
for (int i = 0; i < 4; i++)
|
||||
assert(fabs(output[i] - (i * 4 + 24)) < EPSILON);
|
||||
// second tensor check
|
||||
for (int i = 0; i < 4; i++)
|
||||
assert(fabs(output[i + 4] - (i + 6)) < EPSILON);
|
||||
|
||||
free(input.dim);
|
||||
free(input.input_tensor);
|
||||
free(output);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
printf("################### Testing sum...\n");
|
||||
test_sum();
|
||||
printf("################### Testing max...\n");
|
||||
test_max();
|
||||
printf("################### Testing average...\n");
|
||||
test_average();
|
||||
printf("################### Testing multiple dimensions...\n");
|
||||
test_mult_dimensions();
|
||||
printf("################### Testing multiple outputs...\n");
|
||||
test_mult_outputs();
|
||||
|
||||
printf("Tests: passed!\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user