Factor out the dummy test delegate from simple_delegate_test and create relevant BUILD rules to show

1. how a customized TfLiteDelegate API looks like
2. how this customized TfLiteDelegate could be plugged into TFLite benchmark and task evaluation tools.

PiperOrigin-RevId: 315821839
Change-Id: Ia1c5d13bd1711a88786f36e2ef7527497a6391c7
This commit is contained in:
Chao Mei 2020-06-10 20:18:56 -07:00 committed by TensorFlower Gardener
parent 1248bc9c61
commit 51fe6f6acd
8 changed files with 339 additions and 86 deletions

View File

@ -26,10 +26,10 @@ cc_test(
name = "simple_delegate_test",
srcs = ["simple_delegate_test.cc"],
deps = [
":simple_delegate",
"//tensorflow/lite:framework",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite/c:common",
"//tensorflow/lite/delegates/utils/dummy_delegate",
"//tensorflow/lite/kernels:builtin_ops",
"@com_google_googletest//:gtest_main",
],

View File

@ -0,0 +1,78 @@
load("//tensorflow/lite:build_def.bzl", "tflite_copts")
load("//tensorflow/lite/tools/evaluation/tasks:build_def.bzl", "task_linkopts")
package(
default_visibility = [
"//visibility:public",
],
licenses = ["notice"], # Apache 2.0
)
cc_library(
name = "dummy_delegate",
srcs = [
"dummy_delegate.cc",
],
hdrs = [
"dummy_delegate.h",
],
deps = [
"//tensorflow/lite/c:common",
"//tensorflow/lite/delegates/utils:simple_delegate",
],
)
#### The following are for using the dummy test delegate in TFLite tooling ####
cc_library(
name = "dummy_delegate_provider",
srcs = ["dummy_delegate_provider.cc"],
copts = tflite_copts(),
deps = [
":dummy_delegate",
"//tensorflow/lite/tools/delegates:delegate_provider_hdr",
],
alwayslink = 1,
)
cc_binary(
name = "benchmark_model_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/benchmark:benchmark_model_main",
],
)
cc_binary(
name = "inference_diff_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/inference_diff:run_eval_lib",
],
)
cc_binary(
name = "imagenet_classification_eval_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/imagenet_image_classification:run_eval_lib",
],
)
cc_binary(
name = "coco_object_detection_eval_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/coco_object_detection:run_eval_lib",
],
)

View File

@ -0,0 +1,11 @@
#Description
A dummy delegate implementation to illustrate
* How to use [SimpleDelegateInterface and SimpleDelegateKernelInterface](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/delegates/utils/simple_delegate.h)
to faciliate a TFLite delgate creation. A more sophisticated example could be
refered to the [Flex delegate](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/flex)
* How to leverage the [delegate registrar](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/delegates)
to plug in a delegate in TFLite benchmark and task evaluation tools.
More detailed guide is coming soon.

View File

@ -0,0 +1,99 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed 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.
==============================================================================*/
#include "tensorflow/lite/delegates/utils/dummy_delegate/dummy_delegate.h"
#include <utility>
#include "tensorflow/lite/delegates/utils/simple_delegate.h"
namespace tflite {
namespace dummy_test {
// Dummy delegate kernel.
class DummyDelegateKernel : public SimpleDelegateKernelInterface {
public:
explicit DummyDelegateKernel(const DummyDelegateOptions& options)
: options_(options) {}
TfLiteStatus Init(TfLiteContext* context,
const TfLiteDelegateParams* params) override {
return !options_.error_during_init ? kTfLiteOk : kTfLiteError;
}
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) override {
return !options_.error_during_prepare ? kTfLiteOk : kTfLiteError;
}
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) override {
return !options_.error_during_invoke ? kTfLiteOk : kTfLiteError;
}
private:
const DummyDelegateOptions options_;
};
// DummyDelegate implements the interface of SimpleDelegateInterface.
// This holds the Delegate capabilities.
class DummyDelegate : public SimpleDelegateInterface {
public:
explicit DummyDelegate(const DummyDelegateOptions& options)
: options_(options) {}
bool IsNodeSupportedByDelegate(const TfLiteRegistration* registration,
const TfLiteNode* node,
TfLiteContext* context) const override {
return options_.allowed_builtin_code == registration->builtin_code;
}
TfLiteStatus Initialize(TfLiteContext* context) override { return kTfLiteOk; }
const char* Name() const override {
static constexpr char kName[] = "DummyDelegate";
return kName;
}
std::unique_ptr<SimpleDelegateKernelInterface> CreateDelegateKernelInterface()
override {
return std::make_unique<DummyDelegateKernel>(options_);
}
private:
const DummyDelegateOptions options_;
};
} // namespace dummy_test
} // namespace tflite
DummyDelegateOptions TfLiteDummyDelegateOptionsDefault() {
DummyDelegateOptions options = {0};
// Just assign an invalid builtin code so that this dummy test delegate will
// not support any node by default.
options.allowed_builtin_code = -1;
return options;
}
// Creates a new delegate instance that need to be destroyed with
// `TfLiteDummyDelegateDelete` when delegate is no longer used by TFLite.
// When `options` is set to `nullptr`, the above default values are used:
TfLiteDelegate* TfLiteDummyDelegateCreate(const DummyDelegateOptions* options) {
std::unique_ptr<tflite::dummy_test::DummyDelegate> dummy(
new tflite::dummy_test::DummyDelegate(
options ? *options : TfLiteDummyDelegateOptionsDefault()));
return tflite::TfLiteDelegateFactory::CreateSimpleDelegate(std::move(dummy));
}
// Destroys a delegate created with `TfLiteDummyDelegateCreate` call.
void TfLiteDummyDelegateDelete(TfLiteDelegate* delegate) {
tflite::TfLiteDelegateFactory::DeleteSimpleDelegate(delegate);
}

View File

@ -0,0 +1,60 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed 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.
==============================================================================*/
#ifndef TENSORFLOW_LITE_DELEGATES_UTILS_DUMMY_DELEGATE_DUMMY_DELEGATE_H_
#define TENSORFLOW_LITE_DELEGATES_UTILS_DUMMY_DELEGATE_DUMMY_DELEGATE_H_
#include <memory>
#include "tensorflow/lite/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct {
// Allowed ops to delegate.
int allowed_builtin_code;
// Report error during init.
bool error_during_init;
// Report error during prepare.
bool error_during_prepare;
// Report error during invoke.
bool error_during_invoke;
} DummyDelegateOptions;
// Returns a structure with the default delegate options.
DummyDelegateOptions TfLiteDummyDelegateOptionsDefault();
// Creates a new delegate instance that needs to be destroyed with
// `TfLiteDummyDelegateDelete` when delegate is no longer used by TFLite.
// When `options` is set to `nullptr`, the above default values are used:
TfLiteDelegate* TfLiteDummyDelegateCreate(const DummyDelegateOptions* options);
// Destroys a delegate created with `TfLiteDummyDelegateCreate` call.
void TfLiteDummyDelegateDelete(TfLiteDelegate* delegate);
#ifdef __cplusplus
}
#endif // __cplusplus
// A convenient wrapper that returns C++ std::unique_ptr for automatic memory
// management.
inline std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>
TfLiteDummyDelegateCreateUnique(const DummyDelegateOptions* options) {
return std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>(
TfLiteDummyDelegateCreate(options), TfLiteDummyDelegateDelete);
}
#endif // TENSORFLOW_LITE_DELEGATES_UTILS_DUMMY_DELEGATE_DUMMY_DELEGATE_H_

View File

@ -0,0 +1,61 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed 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.
==============================================================================*/
#include <string>
#include "tensorflow/lite/delegates/utils/dummy_delegate/dummy_delegate.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
namespace tflite {
namespace tools {
class DummyDelegateProvider : public DelegateProvider {
public:
DummyDelegateProvider() {
default_params_.AddParam("use_dummy_delegate",
ToolParam::Create<bool>(false));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::string GetName() const final { return "DummyDelegate"; }
};
REGISTER_DELEGATE_PROVIDER(DummyDelegateProvider);
std::vector<Flag> DummyDelegateProvider::CreateFlags(ToolParams* params) const {
std::vector<Flag> flags = {CreateFlag<bool>("use_dummy_delegate", params,
"use the dummy delegate.")};
return flags;
}
void DummyDelegateProvider::LogParams(const ToolParams& params) const {
TFLITE_LOG(INFO) << "Use dummy test delegate : ["
<< params.Get<bool>("use_dummy_delegate") << "]";
}
TfLiteDelegatePtr DummyDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
if (params.Get<bool>("use_dummy_delegate")) {
auto default_options = TfLiteDummyDelegateOptionsDefault();
return TfLiteDummyDelegateCreateUnique(&default_options);
}
return TfLiteDelegatePtr(nullptr, [](TfLiteDelegate*) {});
}
} // namespace tools
} // namespace tflite

View File

@ -12,82 +12,18 @@ 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.
==============================================================================*/
#include "tensorflow/lite/delegates/utils/simple_delegate.h"
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/delegates/utils/dummy_delegate/dummy_delegate.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/kernels/builtin_op_kernels.h"
namespace tflite {
namespace {
// Delegate options.
struct TestSimpleDelegateOptions {
// Allowed ops to delegate.
int allowed_builtin_code;
// Report error during init.
bool error_during_init = false;
// Report error during prepare.
bool error_during_prepare = false;
// Report error during invoke.
bool error_during_invoke = false;
};
// Dummy delegate kernel.
class TestSimpleDelegateKernel : public SimpleDelegateKernelInterface {
public:
explicit TestSimpleDelegateKernel(TestSimpleDelegateOptions options)
: options_(options) {}
TfLiteStatus Init(TfLiteContext* context,
const TfLiteDelegateParams* params) override {
return !options_.error_during_init ? kTfLiteOk : kTfLiteError;
}
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) override {
return !options_.error_during_prepare ? kTfLiteOk : kTfLiteError;
}
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) override {
return !options_.error_during_invoke ? kTfLiteOk : kTfLiteError;
}
private:
TestSimpleDelegateOptions options_;
};
// Simple delegate which implements the interface of SimpleDelegateInterface.
// This holds the Delegate capabilities.
class TestSimpleDelegate : public SimpleDelegateInterface {
public:
explicit TestSimpleDelegate(TestSimpleDelegateOptions options)
: options_(options) {}
bool IsNodeSupportedByDelegate(const TfLiteRegistration* registration,
const TfLiteNode* node,
TfLiteContext* context) const override {
return options_.allowed_builtin_code == registration->builtin_code;
}
TfLiteStatus Initialize(TfLiteContext* context) override { return kTfLiteOk; }
const char* Name() const override {
static constexpr char kName[] = "TestSimpleDelegate";
return kName;
}
std::unique_ptr<SimpleDelegateKernelInterface> CreateDelegateKernelInterface()
override {
return std::make_unique<TestSimpleDelegateKernel>(options_);
}
private:
TestSimpleDelegateOptions options_;
};
class TestDelegate : public ::testing::Test {
protected:
void SetUp() override {
@ -125,16 +61,15 @@ class TestDelegate : public ::testing::Test {
};
TEST_F(TestDelegate, BasicDelegate) {
TestSimpleDelegateOptions options;
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
options.allowed_builtin_code = kTfLiteBuiltinAdd;
auto delegate = TfLiteDelegateFactory::Create(
std::make_unique<TestSimpleDelegate>(options));
auto delegate = TfLiteDummyDelegateCreateUnique(&options);
interpreter_->ModifyGraphWithDelegate(std::move(delegate));
ASSERT_EQ(interpreter_->execution_plan().size(), 1);
int node = interpreter_->execution_plan()[0];
const auto* node_and_reg = interpreter_->node_and_registration(node);
EXPECT_STREQ("TestSimpleDelegate", node_and_reg->second.custom_name);
EXPECT_STREQ("DummyDelegate", node_and_reg->second.custom_name);
EXPECT_EQ(1, node_and_reg->second.version);
const TfLiteDelegateParams* params = static_cast<const TfLiteDelegateParams*>(
@ -154,42 +89,38 @@ TEST_F(TestDelegate, BasicDelegate) {
}
TEST_F(TestDelegate, NoNodesToDelegate) {
TestSimpleDelegateOptions options;
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
options.allowed_builtin_code = kTfLiteBuiltinSub;
auto delegate = TfLiteDelegateFactory::Create(
std::make_unique<TestSimpleDelegate>(options));
auto delegate = TfLiteDummyDelegateCreateUnique(&options);
interpreter_->ModifyGraphWithDelegate(std::move(delegate));
ASSERT_EQ(interpreter_->execution_plan().size(), 3);
}
TEST_F(TestDelegate, DelegateFailedPrepare) {
TestSimpleDelegateOptions options;
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
options.allowed_builtin_code = kTfLiteBuiltinAdd;
options.error_during_prepare = true;
auto delegate = TfLiteDelegateFactory::Create(
std::make_unique<TestSimpleDelegate>(options));
auto delegate = TfLiteDummyDelegateCreateUnique(&options);
ASSERT_EQ(kTfLiteDelegateError,
interpreter_->ModifyGraphWithDelegate(std::move(delegate)));
}
TEST_F(TestDelegate, DelegateFailedInvoke) {
TestSimpleDelegateOptions options;
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
options.allowed_builtin_code = kTfLiteBuiltinAdd;
options.error_during_invoke = true;
auto delegate = TfLiteDelegateFactory::Create(
std::make_unique<TestSimpleDelegate>(options));
auto delegate = TfLiteDummyDelegateCreateUnique(&options);
ASSERT_EQ(kTfLiteOk,
interpreter_->ModifyGraphWithDelegate(std::move(delegate)));
ASSERT_EQ(kTfLiteError, interpreter_->Invoke());
}
TEST_F(TestDelegate, DelegateFailedInit) {
TestSimpleDelegateOptions options;
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
options.allowed_builtin_code = kTfLiteBuiltinAdd;
options.error_during_init = true;
auto delegate = TfLiteDelegateFactory::Create(
std::make_unique<TestSimpleDelegate>(options));
auto delegate = TfLiteDummyDelegateCreateUnique(&options);
ASSERT_EQ(kTfLiteDelegateError,
interpreter_->ModifyGraphWithDelegate(std::move(delegate)));
}

View File

@ -11,12 +11,25 @@ package(
common_copts = ["-Wall"] + tflite_copts()
cc_binary(
name = "benchmark_model",
# We create a library for benchmark_main.cc to faciliate the creation of a
# customized benchmark model binary that only needs linking with extra
# dependency, e.g., enabling creating of benchmark binaries with a custom
# delegate provider.
cc_library(
name = "benchmark_model_main",
srcs = [
"benchmark_main.cc",
],
copts = common_copts,
deps = [
":benchmark_tflite_model_lib",
"//tensorflow/lite/tools:logging",
],
)
cc_binary(
name = "benchmark_model",
copts = common_copts,
linkopts = tflite_linkopts() + select({
"//tensorflow:android": [
"-pie", # Android 5.0 and later supports only PIE
@ -27,8 +40,7 @@ cc_binary(
}),
tags = ["builder_default_android_arm64"],
deps = [
":benchmark_tflite_model_lib",
"//tensorflow/lite/tools:logging",
":benchmark_model_main",
],
)
@ -42,6 +54,7 @@ cc_binary(
"//tensorflow:android": [
"-pie", # Android 5.0 and later supports only PIE
"-lm", # some builtin ops, e.g., tanh, need -lm
"-Wl,--rpath=/data/local/tmp/", # Hexagon delegate libraries should be in /data/local/tmp
],
"//conditions:default": [],
}),