Skip to content

Commit

Permalink
[Runtime] EdgeTPU runtime for Coral Boards (apache#4698)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmoreau89 authored and zhiics committed Mar 2, 2020
1 parent 25a2d84 commit 795cf73
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 54 deletions.
5 changes: 5 additions & 0 deletions cmake/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ set(USE_TFLITE OFF)
# /path/to/tensorflow: tensorflow root path when use tflite library
set(USE_TENSORFLOW_PATH none)

# Possible values:
# - OFF: disable tflite support for edgetpu
# - /path/to/edgetpu: use specific path to edgetpu library
set(USE_EDGETPU OFF)

# Whether use CuDNN
set(USE_CUDNN ON)

Expand Down
9 changes: 9 additions & 0 deletions cmake/modules/contrib/TFLite.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ if(NOT USE_TFLITE STREQUAL "OFF")
list(APPEND RUNTIME_SRCS ${TFLITE_CONTRIB_SRC})
include_directories(${USE_TENSORFLOW_PATH})

# Additional EdgeTPU libs
if (NOT USE_EDGETPU STREQUAL "OFF")
message(STATUS "Build with contrib.edgetpu")
file(GLOB EDGETPU_CONTRIB_SRC src/runtime/contrib/edgetpu/*.cc)
list(APPEND RUNTIME_SRCS ${EDGETPU_CONTRIB_SRC})
include_directories(${USE_EDGETPU}/libedgetpu)
list(APPEND TVM_RUNTIME_LINKER_LIBS ${USE_EDGETPU}/libedgetpu/direct/aarch64/libedgetpu.so.1)
endif()

if (USE_TFLITE STREQUAL "ON")
set(USE_TFLITE ${USE_TENSORFLOW_PATH}/tensorflow/lite/tools/make/gen/*/lib)
endif()
Expand Down
28 changes: 15 additions & 13 deletions python/tvm/contrib/tflite_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .._ffi.function import get_global_func
from ..rpc import base as rpc_base

def create(tflite_model_bytes, ctx):
def create(tflite_model_bytes, ctx, runtime_target='cpu'):
"""Create a runtime executor module given a tflite model and context.
Parameters
----------
Expand All @@ -27,16 +27,25 @@ def create(tflite_model_bytes, ctx):
ctx : TVMContext
The context to deploy the module. It can be local or remote when there
is only one TVMContext.
runtime_target: str
Execution target of TFLite runtime: either `cpu` or `edge_tpu`.
Returns
-------
tflite_runtime : TFLiteModule
Runtime tflite module that can be used to execute the tflite model.
"""
device_type = ctx.device_type

if runtime_target == 'edge_tpu':
runtime_func = "tvm.edgetpu_runtime.create"
else:
runtime_func = "tvm.tflite_runtime.create"

if device_type >= rpc_base.RPC_SESS_MASK:
fcreate = ctx._rpc_sess.get_function("tvm.tflite_runtime.create")
return TFLiteModule(fcreate(bytearray(tflite_model_bytes), ctx))
fcreate = get_global_func("tvm.tflite_runtime.create")
fcreate = ctx._rpc_sess.get_function(runtime_func)
else:
fcreate = get_global_func(runtime_func)

return TFLiteModule(fcreate(bytearray(tflite_model_bytes), ctx))


Expand All @@ -50,20 +59,19 @@ class TFLiteModule(object):
Parameters
----------
module : Module
The interal tvm module that holds the actual tflite functions.
The internal tvm module that holds the actual tflite functions.
Attributes
----------
module : Module
The interal tvm module that holds the actual tflite functions.
The internal tvm module that holds the actual tflite functions.
"""

def __init__(self, module):
self.module = module
self._set_input = module["set_input"]
self._invoke = module["invoke"]
self._get_output = module["get_output"]
self._allocate_tensors = module["allocate_tensors"]

def set_input(self, index, value):
"""Set inputs to the module via kwargs
Expand Down Expand Up @@ -91,12 +99,6 @@ def invoke(self):
"""
self._invoke()

def allocate_tensors(self):
"""Allocate space for all tensors.
"""
self._allocate_tensors()


def get_output(self, index):
"""Get index-th output to out
Expand Down
73 changes: 73 additions & 0 deletions src/runtime/contrib/edgetpu/edgetpu_runtime.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.
*/

/*!
* \file edgetpu_runtime.cc
*/
#include <tvm/runtime/registry.h>
#include <tensorflow/lite/interpreter.h>
#include <tensorflow/lite/kernels/register.h>
#include <tensorflow/lite/model.h>
#include <edgetpu.h>


#include "edgetpu_runtime.h"

namespace tvm {
namespace runtime {

void EdgeTPURuntime::Init(const std::string& tflite_model_bytes,
TVMContext ctx) {
const char* buffer = tflite_model_bytes.c_str();
size_t buffer_size = tflite_model_bytes.size();
// Load compiled model as a FlatBufferModel
std::unique_ptr<tflite::FlatBufferModel> model =
tflite::FlatBufferModel::BuildFromBuffer(buffer, buffer_size);
// Build resolver
tflite::ops::builtin::BuiltinOpResolver resolver;
// Init EdgeTPUContext object
edgetpu_context_ = edgetpu::EdgeTpuManager::GetSingleton()->OpenDevice();
// Add custom edgetpu ops to resolver
resolver.AddCustom(edgetpu::kCustomOp, edgetpu::RegisterCustomOp());
// Build interpreter
TfLiteStatus status = tflite::InterpreterBuilder(*model, resolver)(&interpreter_);
CHECK_TFLITE_STATUS(status) << "Failed to build interpreter.";
// Bind EdgeTPU context with interpreter.
interpreter_->SetExternalContext(kTfLiteEdgeTpuContext, edgetpu_context_.get());
interpreter_->SetNumThreads(1);
// Allocate tensors
status = interpreter_->AllocateTensors();
CHECK_TFLITE_STATUS(status) << "Failed to allocate tensors.";

ctx_ = ctx;
}

Module EdgeTPURuntimeCreate(const std::string& tflite_model_bytes,
TVMContext ctx) {
auto exec = make_object<EdgeTPURuntime>();
exec->Init(tflite_model_bytes, ctx);
return Module(exec);
}

TVM_REGISTER_GLOBAL("tvm.edgetpu_runtime.create")
.set_body([](TVMArgs args, TVMRetValue* rv) {
*rv = EdgeTPURuntimeCreate(args[0], args[1]);
});
} // namespace runtime
} // namespace tvm
66 changes: 66 additions & 0 deletions src/runtime/contrib/edgetpu/edgetpu_runtime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.
*/

/*!
* \brief EdgeTPU runtime that can run tflite model compiled
* for EdgeTPU containing only tvm PackedFunc.
* \file edgetpu_runtime.h
*/
#ifndef TVM_RUNTIME_CONTRIB_EDGETPU_EDGETPU_RUNTIME_H_
#define TVM_RUNTIME_CONTRIB_EDGETPU_EDGETPU_RUNTIME_H_

#include <string>
#include <memory>

#include "../tflite/tflite_runtime.h"

namespace tvm {
namespace runtime {

/*!
* \brief EdgeTPU runtime.
*
* This runtime can be accessed in various languages via
* the TVM runtime PackedFunc API.
*/
class EdgeTPURuntime : public TFLiteRuntime {
public:
/*!
* \return The type key of the executor.
*/
const char* type_key() const final {
return "EdgeTPURuntime";
}

/*!
* \brief Initialize the edge TPU tflite runtime with tflite model and context.
* \param tflite_model_bytes The tflite model.
* \param ctx The context where the tflite model will be executed on.
*/
void Init(const std::string& tflite_model_bytes,
TVMContext ctx);

private:
std::shared_ptr<edgetpu::EdgeTpuContext> edgetpu_context_;
};

} // namespace runtime
} // namespace tvm

#endif // TVM_RUNTIME_CONTRIB_EDGETPU_EDGETPU_RUNTIME_H_
46 changes: 21 additions & 25 deletions src/runtime/contrib/tflite/tflite_runtime.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
* \file tflite_runtime.cc
*/
#include <tvm/runtime/registry.h>
#include <tvm/dtype.h>
#include <tensorflow/lite/interpreter.h>
#include <tensorflow/lite/kernels/register.h>
#include <tensorflow/lite/model.h>
Expand All @@ -33,37 +32,37 @@ namespace tvm {
namespace runtime {

#define TVM_DTYPE_DISPATCH(type, DType, ...) \
if (type == DataType::Float(64)) { \
if (type == DataType::Float(64)) { \
typedef double DType; \
{__VA_ARGS__} \
} else if (type == DataType::Float(32)) { \
} else if (type == DataType::Float(32)) { \
typedef float DType; \
{__VA_ARGS__} \
} else if (type == DataType::Float(16)) { \
} else if (type == DataType::Float(16)) { \
typedef uint16_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::Int(64)) { \
} else if (type == DataType::Int(64)) { \
typedef int64_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::Int(32)) { \
} else if (type == DataType::Int(32)) { \
typedef int32_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::Int(16)) { \
} else if (type == DataType::Int(16)) { \
typedef int16_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::Int(8)) { \
} else if (type == DataType::Int(8)) { \
typedef int8_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::UInt(64)) { \
} else if (type == DataType::UInt(64)) { \
typedef uint64_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::UInt(32)) { \
} else if (type == DataType::UInt(32)) { \
typedef uint32_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::UInt(16)) { \
} else if (type == DataType::UInt(16)) { \
typedef uint16_t DType; \
{__VA_ARGS__} \
} else if (type == DataType::UInt(8)) { \
} else if (type == DataType::UInt(8)) { \
typedef uint8_t DType; \
{__VA_ARGS__} \
} else { \
Expand All @@ -79,9 +78,9 @@ DataType TfLiteDType2TVMDType(TfLiteType dtype) {
case kTfLiteInt64:
return DataType::Int(64);
case kTfLiteInt16:
returnDataType::Int(16);
return DataType::Int(16);
case kTfLiteInt8:
returnDataType::Int(8);
return DataType::Int(8);
case kTfLiteUInt8:
return DataType::UInt(8);
case kTfLiteFloat16:
Expand All @@ -92,20 +91,21 @@ DataType TfLiteDType2TVMDType(TfLiteType dtype) {
}
}


void TFLiteRuntime::Init(const std::string& tflite_model_bytes,
TVMContext ctx) {
const char* buffer = tflite_model_bytes.c_str();
size_t buffer_size = tflite_model_bytes.size();
std::unique_ptr<tflite::FlatBufferModel> model =
tflite::FlatBufferModel::BuildFromBuffer(buffer, buffer_size);
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder(*model, resolver)(&interpreter_);
ctx_ = ctx;
}
// Build interpreter
TfLiteStatus status = tflite::InterpreterBuilder(*model, resolver)(&interpreter_);
CHECK_TFLITE_STATUS(status) << "Failed to build interpreter.";
// Allocate tensors
status = interpreter_->AllocateTensors();
CHECK_TFLITE_STATUS(status) << "Failed to allocate tensors.";

void TFLiteRuntime::AllocateTensors() {
interpreter_->AllocateTensors();
ctx_ = ctx;
}

void TFLiteRuntime::Invoke() {
Expand All @@ -129,7 +129,7 @@ void TFLiteRuntime::SetInput(int index, DLTensor* data_in) {
}

NDArray TFLiteRuntime::GetOutput(int index) const {
TfLiteTensor* output = interpreter_->output_tensor(index);
TfLiteTensor* output = interpreter_->tensor(interpreter_->outputs()[index]);
DataType dtype = TfLiteDType2TVMDType(output->type);
TfLiteIntArray* dims = output->dims;
int64_t size = 1;
Expand Down Expand Up @@ -167,10 +167,6 @@ PackedFunc TFLiteRuntime::GetFunction(
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->Invoke();
});
} else if (name == "allocate_tensors") {
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {
this->AllocateTensors();
});
} else {
return PackedFunc();
}
Expand Down
Loading

0 comments on commit 795cf73

Please sign in to comment.