chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite:build_def.bzl", "tflite_copts", "tflite_copts_warnings")
load("//tensorflow/lite/core/shims:cc_library_with_tflite.bzl", "cc_library_with_tflite", "cc_test_with_tflite")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
common_copts = tflite_copts() + tflite_copts_warnings()
cc_library_with_tflite(
name = "delegate_provider_hdr",
hdrs = [
"delegate_provider.h",
],
compatible_with = get_compatible_with_portable(),
copts = common_copts,
tflite_deps = [
"//tensorflow/lite/c:common",
],
deps = [
"//tensorflow/lite/tools:command_line_flags",
"//tensorflow/lite/tools:logging",
"//tensorflow/lite/tools:tool_params",
],
)
cc_library_with_tflite(
name = "delegate_provider_lib",
srcs = [
"delegate_provider.cc",
],
copts = common_copts,
tflite_deps = [
":delegate_provider_hdr",
],
)
# A convenient library for all inference execution providers.
cc_library_with_tflite(
name = "tflite_execution_providers",
copts = tflite_copts(),
tflite_deps = [
":xnnpack_delegate_provider",
":delegate_provider_lib",
],
deps = [
":coreml_delegate_provider",
":default_execution_provider",
":external_delegate_provider",
":gpu_delegate_provider",
":hexagon_delegate_provider",
":nnapi_delegate_provider",
":ynnpack_delegate_provider",
"//tensorflow/lite/tools/delegates/experimental/stable_delegate:delegate_provider",
],
alwayslink = 1,
)
cc_library_with_tflite(
name = "default_execution_provider",
srcs = ["default_execution_provider.cc"],
copts = tflite_copts(),
linkstatic = True,
tflite_deps = [
":delegate_provider_hdr",
],
visibility = ["//visibility:public"],
alwayslink = 1,
)
cc_library(
name = "gpu_delegate_provider",
srcs = ["gpu_delegate_provider.cc"],
copts = common_copts + select({
"//tensorflow:ios": [
"-xobjective-c++",
],
"//tensorflow:macos_arm64": [
"-xobjective-c++",
],
"//conditions:default": [],
}),
deps = [
":delegate_provider_hdr",
"//tensorflow/lite/tools/evaluation:utils",
] + select({
"//tensorflow/lite/delegates/gpu:supports_gpu_delegate": [
"//tensorflow/lite/delegates/gpu:delegate",
],
"//conditions:default": [],
}) + select({
"//tensorflow:ios": [
"//tensorflow/lite/delegates/gpu:metal_delegate",
],
"//tensorflow:macos_arm64": [
"//tensorflow/lite/delegates/gpu:metal_delegate",
],
"//conditions:default": [],
}),
alwayslink = 1,
)
cc_library(
name = "nnapi_delegate_provider",
srcs = select({
"//tensorflow:windows": [],
"//conditions:default": ["nnapi_delegate_provider.cc"],
}),
copts = common_copts,
deps = select({
"//tensorflow:windows": [],
"//conditions:default": [
":delegate_provider_hdr",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/nnapi:nnapi_implementation",
"//tensorflow/lite/nnapi:nnapi_util",
"//tensorflow/lite/nnapi/sl:nnapi_support_library",
"//tensorflow/lite/nnapi/sl:nnapi_support_library_headers",
],
}),
alwayslink = 1,
)
cc_library(
name = "hexagon_delegate_provider",
srcs = ["hexagon_delegate_provider.cc"],
copts = common_copts,
deps = [
":delegate_provider_hdr",
"//tensorflow/lite/tools/evaluation:utils",
] + select({
"//tensorflow:arm_any": [
"//tensorflow/lite/delegates/hexagon:hexagon_delegate",
],
"//conditions:default": [],
}),
alwayslink = 1,
)
cc_library(
name = "coreml_delegate_provider",
srcs = ["coreml_delegate_provider.cc"],
copts = common_copts + select({
"//tensorflow:ios": [
"-xobjective-c++",
],
"//tensorflow:macos_arm64": [
"-xobjective-c++",
],
"//conditions:default": [],
}),
deps = [
":delegate_provider_hdr",
"//tensorflow/lite/tools/evaluation:utils",
] + select({
"//tensorflow:ios": [
"//tensorflow/lite/delegates/coreml:coreml_delegate",
],
"//tensorflow:macos_arm64": [
"//tensorflow/lite/delegates/coreml:coreml_delegate",
],
"//conditions:default": [],
}),
alwayslink = 1,
)
cc_library_with_tflite(
name = "xnnpack_delegate_provider",
srcs = ["xnnpack_delegate_provider.cc"],
copts = tflite_copts(),
linkstatic = True,
tflite_deps = [
":delegate_provider_hdr",
"//tensorflow/lite/tools/evaluation:utils",
],
visibility = ["//visibility:public"],
deps = [
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"//tensorflow/lite/tools:tool_params",
],
alwayslink = 1,
)
cc_library_with_tflite(
name = "ynnpack_delegate_provider",
srcs = ["ynnpack_delegate_provider.cc"],
copts = tflite_copts(),
linkstatic = True,
tflite_deps = [
":delegate_provider_hdr",
],
visibility = ["//visibility:public"],
deps = [
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/delegates/ynnpack:ynnpack_delegate",
"//tensorflow/lite/tools:command_line_flags",
"//tensorflow/lite/tools:tool_params",
],
alwayslink = 1,
)
cc_test(
name = "xnnpack_delegate_provider_test",
srcs = ["xnnpack_delegate_provider_test.cc"],
copts = tflite_copts(),
visibility = ["//visibility:public"],
deps = [
":delegate_provider_hdr",
":xnnpack_delegate_provider",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"//tensorflow/lite/tools:tool_params",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "external_delegate_provider",
srcs = ["external_delegate_provider.cc"],
copts = tflite_copts(),
linkopts = select({
"//tensorflow:windows": [],
"//conditions:default": ["-ldl"],
}),
linkstatic = True,
visibility = ["//visibility:public"],
deps = [
":delegate_provider_hdr",
"//tensorflow/lite/delegates/external:external_delegate",
],
alwayslink = 1,
)
cc_test_with_tflite(
name = "delegate_provider_lib_test",
size = "small",
srcs = ["delegate_provider_test.cc"],
copts = tflite_copts(),
# See details in https://github.com/bazelbuild/bazel/issues/11552 to avoid
# lazy symbol binding failure on macOS.
linkstatic = select({
"//tensorflow:macos": True,
"//conditions:default": False,
}),
tflite_deps = [
":default_execution_provider",
":delegate_provider_hdr",
":delegate_provider_lib",
":xnnpack_delegate_provider",
"//tensorflow/lite/c:test_util",
],
deps = [
":nnapi_delegate_provider",
"//tensorflow/lite/delegates/utils/dummy_delegate:dummy_delegate_provider",
"//tensorflow/lite/tools:tool_params",
"@com_google_googletest//:gtest_main",
],
)
# copybara:uncomment_begin(google-only)
# cc_library(
# name = "hexagon_ynn_delegate_provider",
# srcs = ["hexagon_ynn_delegate_provider.cc"],
# copts = common_copts,
# deps = [
# ":delegate_provider_hdr",
# "//tensorflow/lite/tools:command_line_flags",
# "//tensorflow/lite/tools:tool_params",
# ] + select({
# "//tensorflow:arm_any": [
# "//tensorflow/lite/delegates/hexagon_ynn:hexagon_ynn_delegate",
# ],
# "//conditions:default": [],
# }),
# alwayslink = 1,
# )
# copybara:uncomment_end
+208
View File
@@ -0,0 +1,208 @@
# TFLite Delegate Utilities for Tooling
## TFLite Delegate Registrar
[A TFLite delegate registrar](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/tools/delegates/delegate_provider.h)
is provided here. The registrar keeps a list of TFLite delegate providers, each
of which defines a list parameters that could be initialized from commandline
arguments and provides a TFLite delegate instance creation based on those
parameters. This delegate registrar has been used in TFLite evaluation tools and
the benchmark model tool.
A particular TFLite delegate provider can be used by linking the corresponding
library, e.g. adding it to the `deps` of a BUILD rule. Note that each delegate
provider library has been configured with `alwayslink=1` in the BUILD rule so
that it will be linked to any binary that directly or indirectly depends on it.
The following lists all implemented TFLite delegate providers and their
corresponding list of parameters that each supports to create a particular
TFLite delegate.
### Common parameters
* `num_threads`: `int` (default=-1) \
The number of threads to use for running the inference on CPU. By default,
this is set to the platform default value -1.
* `max_delegated_partitions`: `int` (default=0, i.e. no limit) \
The maximum number of partitions that will be delegated. \
Currently supported by the GPU, Hexagon, CoreML and NNAPI delegate.
* `min_nodes_per_partition`: `int` (default=delegate's own choice) \
The minimal number of TFLite graph nodes of a partition that needs to be
reached to be delegated. A negative value or 0 means to use the default
choice of each delegate. \
This option is currently supported by the Hexagon and CoreML delegate.
* `delegate_serialize_dir`: `string` (default="") \
Directory to be used by delegates for serializing any model data. This
allows the delegate to save data into this directory to reduce init time
after the first run. Currently supported by GPU (OpenCL) and NNAPI delegate
with specific backends on Android. Note that delegate_serialize_token is
also required to enable this feature.
* `delegate_serialize_token`: `string` (default="") \
Model-specific token acting as a namespace for delegate serialization.
Unique tokens ensure that the delegate doesn't read inapplicable/invalid
data. Note that delegate_serialize_dir is also required to enable this
feature.
* `first_delegate_node_index`: `int` (default=0) \
The index of the first node that could be delegated. Debug only. Add
'--define=tflite_debug_delegate=true' in your build command line to use it.
\
Currently only supported by CoreML delegate.
* `last_delegate_node_index`: `int` (default=INT_MAX) \
The index of the last node that could be delegated. Debug only. Add
'--define=tflite_debug_delegate=true' in your build command line to use it.
\
Currently only supported by CoreML delegate.
### GPU delegate provider
The GPU delegate is supported on Android and iOS devices, or platforms where the
delegate library is built with "-DCL_DELEGATE_NO_GL" macro.
#### Common options
* `use_gpu`: `bool` (default=false) \
Whether to use the
[GPU accelerator delegate](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/gpu).
* `gpu_precision_loss_allowed`: `bool` (default=true) \
Whether to allow the GPU delegate to carry out computation with some
precision loss (i.e. processing in FP16) or not. If allowed, the performance
will increase.
* `gpu_experimental_enable_quant`: `bool` (default=true) \
Whether to allow the GPU delegate to run a 8-bit quantized model or not.
* `gpu_inference_for_sustained_speed`: `bool` (default=false) \
Whether to prefer maximizing the throughput. This mode will help when the
same delegate will be used repeatedly on multiple inputs. This is supported
on non-iOS platforms.
#### Android options
* `gpu_backend`: `string` (default="") \
Force the GPU delegate to use a particular backend for execution, and fail
if unsuccessful. Should be one of: cl, gl. By default, the GPU delegate will
try OpenCL first and then OpenGL if the former fails.
#### iOS options
* `gpu_wait_type`: `string` (default="") \
Which GPU wait_type option to use. Should be one of the following: passive,
active, do_not_wait, aggressive. When left blank, passive mode is used by
default.
### NNAPI delegate provider
* `use_nnapi`: `bool` (default=false) \
Whether to use
[Android NNAPI](https://developer.android.com/ndk/guides/neuralnetworks/).
This API is available on recent Android devices. When on Android Q+, will
also print the names of NNAPI accelerators accessible through the
`nnapi_accelerator_name` flag.
* `nnapi_accelerator_name`: `string` (default="") \
The name of the NNAPI accelerator to use (requires Android Q+). If left
blank, NNAPI will automatically select which of the available accelerators
to use.
* `nnapi_execution_preference`: `string` (default="") \
Which
[NNAPI execution preference](https://developer.android.com/ndk/reference/group/neural-networks.html#group___neural_networks_1gga034380829226e2d980b2a7e63c992f18af727c25f1e2d8dcc693c477aef4ea5f5)
to use when executing using NNAPI. Should be one of the following:
fast_single_answer, sustained_speed, low_power, undefined.
* `nnapi_execution_priority`: `string` (default="") \
The relative priority for executions of the model in NNAPI. Should be one of
the following: default, low, medium and high. This option requires Android
11+.
* `disable_nnapi_cpu`: `bool` (default=true) \
Excludes the
[NNAPI CPU reference implementation](https://developer.android.com/ndk/guides/neuralnetworks#device-assignment)
from the possible devices to be used by NNAPI to execute the model. This
option is ignored if `nnapi_accelerator_name` is specified.
* `nnapi_allow_fp16`: `bool` (default=false) \
Whether to allow FP32 computation to be run in FP16.
* `nnapi_allow_dynamic_dimensions`: `bool` (default=false) \
Whether to allow dynamic dimension sizes without re-compilation. This
requires Android 9+.
* `nnapi_use_burst_mode`: `bool` (default=false) \
use NNAPI Burst mode if supported. Burst mode allows accelerators to
efficiently manage resources, which would significantly reduce overhead
especially if the same delegate instance is to be used for multiple
inferences.
* `nnapi_support_library_path`: `string` (default=""), Path from which NNAPI
support library will be loaded to construct the delegate. In order to use
NNAPI delegate with support library, --nnapi_accelerator_name must be
specified and must be equal to one of the devices provided by the support
library.
### Hexagon delegate provider
* `use_hexagon`: `bool` (default=false) \
Whether to use the Hexagon delegate. Not all devices may support the Hexagon
delegate, refer to the
[TensorFlow Lite documentation](https://www.tensorflow.org/lite/performance/hexagon_delegate)
for more information about which devices/chipsets are supported and about
how to get the required libraries. To use the Hexagon delegate also build
the hexagon_nn:libhexagon_interface.so target and copy the library to the
device. All libraries should be copied to /data/local/tmp on the device.
* `hexagon_profiling`: `bool` (default=false) \
Whether to profile ops running on hexagon.
### XNNPACK delegate provider
* `use_xnnpack`: `bool` (default=false) \
Whether to explicitly apply the XNNPACK delegate. Note the XNNPACK delegate
could be implicitly applied by the TF Lite runtime regardless the value of
this parameter. To disable this implicit application, set the value to
`false` explicitly.
* `xnnpack_force_fp16`: `bool` (default=false) \
Enforce float16 inference. Internaly, set flag
`TFLITE_XNNPACK_DELEGATE_FLAG_FORCE_FP16` on XNNPackDelegateOptions.
### CoreML delegate provider
* `use_coreml`: `bool` (default=false) \
Whether to use the
[Core ML delegate](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/coreml).
This option is only available in iOS.
* `coreml_version`: `int` (default=0) \
Target Core ML version for model conversion. The default value is 0 and it
means using the newest version that's available on the device.
### External delegate provider
* `external_delegate_path`: `string` (default="") \
Path to the external delegate library to use.
* `external_delegate_options`: `string` (default="") \
A list of options to be passed to the external delegate library. Options
should be in the format of `option1:value1;option2:value2;optionN:valueN`
### Stable delegate provider [Experimental API]
The stable delegate provider provides a `TfLiteOpaqueDelegate` object pointer
and its corresponding deleter by loading a dynamic library that encapsulates the
actual TFLite delegate implementation in a
[`TfLiteStableDelegate`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/core/acceleration/configuration/c/stable_delegate.h)
struct instance.
While the structure of the stable delegate provider is similar to the external
delegate provider, which provides the
[external delegates](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/external),
the design objectives of the stable delegates and the external delegates are
different.
- Stable delegates are designed to work with shared object files that support
ABI backward compatibility; that is, the delegate and the TF Lite runtime
won't need to be built using the exact same version of TF Lite as the app.
However, this is work in progress and the ABI stability is not yet
guaranteed.
- External delegates were developed mainly for delegate evaluation
(https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/external).
The stable delegates and the external delegates use different APIs for
diagnosing errors, creating and destroying the delegates. For more details of
the concrete API differences, please check
[stable_delegate.h](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/core/acceleration/configuration/c/stable_delegate.h)
and
[external_delegate.h](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/delegates/external/external_delegate.h).
The stable delegate provider is not supported on Windows platform.
* `stable_abi_delegate_settings_file`: `string` (default="") \
Path to the delegate settings JSON file.
@@ -0,0 +1,4 @@
# TFLite Delegate Compatibility Checker [Experimental]
The TFLite Delegate Compatibility Checker (DCC) checks compatibility for
different delegates including GPU, NNAPI, etc.
@@ -0,0 +1,50 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "online_helper_delegate",
srcs = ["online_helper_delegate.cc"],
hdrs = ["online_helper_delegate.h"],
deps = [
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "delegate_compatibility_checker_base",
srcs = ["delegate_compatibility_checker_base.cc"],
hdrs = [
"delegate_compatibility_checker_base.h",
],
deps = [
":delegate_compatibility_checker_util",
"//tensorflow/lite:framework_stable",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/kernels:builtin_ops",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "delegate_compatibility_checker_util",
hdrs = [
"delegate_compatibility_checker_util.h",
],
)
@@ -0,0 +1,62 @@
/* Copyright 2022 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/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include <cstdlib>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_util.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
absl::Status DelegateCompatibilityCheckerBase::checkModelCompatibilityOffline(
tflite::FlatBufferModel* model_buffer, proto::CompatibilityResult* result) {
auto model = model_buffer->GetModel();
auto subgraphs = model->subgraphs();
for (int i = 0; i < subgraphs->Length(); ++i) {
const tflite::SubGraph* subgraph = subgraphs->Get(i);
for (int j = 0; j < subgraph->operators()->Length(); ++j) {
proto::OpCompatibilityResult* op_result =
result->add_compatibility_results();
op_result->set_subgraph_index_in_model(i);
op_result->set_operator_index_in_subgraph(j);
const tflite::Operator* op = subgraph->operators()->Get(j);
const tflite::OperatorCode* op_code =
model->operator_codes()->Get(op->opcode_index());
RETURN_IF_ERROR(
checkOpCompatibilityOffline(op_code, op, subgraph, model, op_result));
}
}
return absl::OkStatus();
}
absl::Status DelegateCompatibilityCheckerBase::checkOpCompatibilityOffline(
const tflite::OperatorCode* op_code, const tflite::Operator* op,
const tflite::SubGraph* subgraph, const tflite::Model* model,
proto::OpCompatibilityResult* op_result) {
OpSignature op_sig = tflite::GetOpSignature(op_code, op, subgraph, model);
auto status = checkOpSigCompatibility(op_sig, op_result);
free(op_sig.builtin_data);
return status;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,101 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/core/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
// TODO(b/243489631): Remove online mode support.
// Base class for the delegate compatibility checker (DCC). Extracts the logic
// of iterating through the model, and lets each specific DCC to check if the
// operation of a node is compatible with that delegate. TfLiteNode and
// operator (in schema.fbs) are equivalent.
// There are two modes supported: online and offline. Online mode needs
// TfLiteContext while offline mode doesn't. Online mode is supported as an
// intermediate stage and it is recommended that delegates support offline
// mode in validation logic, if possible.
class DelegateCompatibilityCheckerBase {
public:
virtual ~DelegateCompatibilityCheckerBase() = default;
// Iterates over the subgraphs in the model, and for each operator, checks if
// it is compatible with the delegate.
// Stores the compatibility for each operator in the result structure.
absl::Status checkModelCompatibilityOffline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result);
// This function is implemented differently by each specific DCC.
// Stores the compatibility for each operator in the result structure.
virtual absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) = 0;
// This function gets the operation signature (OpSignature) from the
// op_code, op, subgraph and model, and then call to checkOpSigCompatibility()
// with the operation signature.
// Params:
// op_code: Used to get the built in code of the operator, in
// order to know which operator is being used (e.g. MUL,
// FULLY_CONNECTED…).
// op: Used to get the input and output tensors indexes, so that obtains the
// tensors with the subgraph.
// subgraph: Used to get the tensors to finally get the OpSignature.
// model: Used to get the buffer in order to check if the tensor is
// a constant tensor.
// op_result: Stores whether the operation is compatible or not and why.
// Returns: absl::OkStatus() if the function is completed without exceptions.
absl::Status checkOpCompatibilityOffline(
const tflite::OperatorCode* op_code, const tflite::Operator* op,
const tflite::SubGraph* subgraph, const tflite::Model* model,
tflite::proto::OpCompatibilityResult* op_result);
// Returns the dictionary with the initialized keys. This is an abstract
// function because each specific DCC needs different parameters, which will
// be the keys in the returned dictionary.
virtual std::unordered_map<std::string, std::string>
getDccConfigurations() = 0;
// Sets the parameters needed in the specific DCC. Also checks if the
// value types are correct.
virtual absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) = 0;
private:
// This function is implemented differently by each specific DCC because
// they contain the logic for checking if the operation in op_sig is
// compatible for that specific DCC. op_result stores whether the operation
// is supported or not, and why. By using offline mode, only op_signature is
// used to perform the checks.
virtual absl::Status checkOpSigCompatibility(
const tflite::OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) = 0;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
@@ -0,0 +1,31 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
namespace tflite {
namespace tools {
#define RETURN_IF_ERROR(s) \
{ \
auto c = (s); \
if (!c.ok()) return c; \
}
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
@@ -0,0 +1,61 @@
/* Copyright 2022 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/tools/delegates/compatibility/common/online_helper_delegate.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
TfLiteStatus OnlineHelperDelegate::DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate) {
auto self = reinterpret_cast<OnlineHelperDelegate*>(delegate);
// Gets execution plan.
TfLiteIntArray* execution_plan;
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
// Validates compatibility for each node.
for (auto node_index : TfLiteIntArrayView(execution_plan)) {
proto::OpCompatibilityResult* op_result =
self->result_->add_compatibility_results();
TfLiteNode* node;
TfLiteRegistration* registration;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(
context, node_index, &node, &registration));
// Always subgraph #0 because in the interpreter (and TfLiteContext), the
// methods refer to the primary_subgraph, so the only subgraph available is
// the subgraph #0.
op_result->set_subgraph_index_in_model(0);
op_result->set_operator_index_in_subgraph(node_index);
auto status = self->check_op_func_ptr_(context, node, registration,
self->dcc_configs_, op_result);
if (!status.ok()) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Error at node %d: %s", node_index,
status.message());
return kTfLiteError;
}
}
return kTfLiteOk;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,97 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
#include <functional>
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
// This utility delegate is required because some TfLiteContext related
// functions are forbidden if not calling in delegate. This class is only used
// for online mode.
// WARNING: This is an experimental class and subject to change.
class OnlineHelperDelegate : public TfLiteDelegate {
public:
OnlineHelperDelegate(
std::unordered_map<std::string, std::string>& dcc_configs,
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>&,
proto::OpCompatibilityResult*)>
check_op_func_ptr,
proto::CompatibilityResult* result)
: TfLiteDelegate(TfLiteDelegateCreate()),
result_(result),
dcc_configs_(dcc_configs),
check_op_func_ptr_(check_op_func_ptr) {
Prepare = DoPrepare;
CopyFromBufferHandle = DoCopyFromBufferHandle;
CopyToBufferHandle = DoCopyToBufferHandle;
FreeBufferHandle = DoFreeBufferHandle;
data_ = &delegate_data_;
}
protected:
// This function uses a pointer to a method (implemented by each specific DCC)
// which contains the logic to check whether the primary subgraph can be
// delegated to the specific delegate.
static TfLiteStatus DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate);
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// There is no buffer handle in this delegate.
static void DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle* handle) {}
private:
int delegate_data_;
proto::CompatibilityResult* result_;
std::unordered_map<std::string, std::string> dcc_configs_;
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>&,
proto::OpCompatibilityResult*)>
check_op_func_ptr_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
@@ -0,0 +1,48 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_test(
name = "gpu_delegate_compatibility_checker_test",
srcs = [
"gpu_delegate_compatibility_checker_test.cc",
],
data = [
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/conv3d_huge_im2col.bin",
],
tags = ["no_oss"], #TODO(b/276295784): Re-enable when fixed.
deps = [
":gpu_delegate_compatibility_checker",
"//tensorflow/core/platform:resource_loader",
"//tensorflow/lite:model_builder",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "gpu_delegate_compatibility_checker",
srcs = ["gpu_delegate_compatibility_checker.cc"],
hdrs = [
"gpu_delegate_compatibility_checker.h",
],
deps = [
"//tensorflow/lite:model_builder",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_base",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:gpu_compatibility",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
],
)
@@ -0,0 +1,95 @@
/* Copyright 2022 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/tools/delegates/compatibility/gpu/gpu_delegate_compatibility_checker.h"
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/gpu_compatibility.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
namespace {
void convertToValidationFailureType(absl::Status status,
proto::OpCompatibilityResult* op_result) {
auto compatibility_failure = op_result->add_compatibility_failures();
compatibility_failure->set_description(std::string(status.message()));
switch (status.code()) {
case absl::StatusCode::kInvalidArgument:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INVALID_ARGUMENT);
break;
case absl::StatusCode::kUnimplemented:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNIMPLEMENTED_ERROR);
break;
case absl::StatusCode::kInternal:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
break;
case absl::StatusCode::kOutOfRange:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_OUT_OF_RANGE);
break;
default:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
compatibility_failure->set_description(
"Unknown validation failure type.");
}
}
} // namespace
std::unordered_map<std::string, std::string>
tools::GpuDelegateCompatibilityChecker::getDccConfigurations() {
return {};
}
absl::Status tools::GpuDelegateCompatibilityChecker::setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) {
return absl::OkStatus();
}
absl::Status
tools::GpuDelegateCompatibilityChecker::checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) {
return absl::UnimplementedError(
"Online mode is not supported on GPU delegate compatibility checker.");
}
absl::Status tools::GpuDelegateCompatibilityChecker::checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) {
auto status = CheckGpuDelegateCompatibility(op_sig);
if (!status.ok()) {
convertToValidationFailureType(status, op_result);
op_result->set_is_supported(false);
} else {
op_result->set_is_supported(true);
}
return absl::OkStatus();
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,59 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
// Class to check if an operation or a model is compatible with GPU delegate.
// No supported parameters.
class GpuDelegateCompatibilityChecker
: public DelegateCompatibilityCheckerBase {
public:
GpuDelegateCompatibilityChecker() {}
// Online mode is not supported in the GPU delegate compatibility checker.
absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) override;
// No parameters are supported, no need to call to this function.
std::unordered_map<std::string, std::string> getDccConfigurations() override;
// No parameters are supported, no need to call to this function.
absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) override;
private:
absl::Status checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) override;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
@@ -0,0 +1,114 @@
/* Copyright 2022 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/tools/delegates/compatibility/gpu/gpu_delegate_compatibility_checker.h"
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "tensorflow/core/platform/resource_loader.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok());
#endif
namespace {
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
// Builds interpreter.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
} // namespace
TEST(GpuDelegateCompatibilityCheckerTest, CheckOnlineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
// Online mode is not supported by GPU DCC
EXPECT_EQ(
gpu_dcc
.checkModelCompatibilityOnline(fb_model.get(), &compatibility_result)
.code(),
absl::StatusCode::kUnimplemented);
}
TEST(GpuDelegateCompatibilityCheckerTest, CompatibleModelOfflineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
EXPECT_OK(gpu_dcc.checkModelCompatibilityOffline(fb_model.get(),
&compatibility_result));
for (auto op_compatibility_result :
compatibility_result.compatibility_results()) {
EXPECT_TRUE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result.compatibility_results_size(), 2);
}
TEST(GpuDelegateCompatibilityCheckerTest, IncompatibleModelOfflineMode) {
const std::string& full_path = tensorflow::GetDataDependencyFilepath(
"tensorflow/lite/testdata/conv3d_huge_im2col.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
EXPECT_OK(gpu_dcc.checkModelCompatibilityOffline(fb_model.get(),
&compatibility_result));
for (auto op_compatibility_result :
compatibility_result.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result.compatibility_results_size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,87 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
# BUILD rules for NNAPI delegate compatibility checking.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "nnapi_compatibility_lib",
srcs = [
"nnapi_compatibility_lib.cc",
],
hdrs = [
"nnapi_compatibility_lib.h",
],
deps = [
"//tensorflow/lite:framework_stable",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
],
)
cc_library(
name = "nnapi_delegate_compatibility_checker",
srcs = ["nnapi_delegate_compatibility_checker.cc"],
hdrs = [
"nnapi_delegate_compatibility_checker.h",
],
copts = ["-DNNAPI_VERBOSE_VALIDATION"],
deps = [
"//tensorflow/lite:framework_stable",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core:cc_api_stable",
"//tensorflow/lite/core/kernels:builtin_ops",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/nnapi:nnapi_lib",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_base",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_util",
"//tensorflow/lite/tools/delegates/compatibility/common:online_helper_delegate",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "nnapi_compatibility_lib_test",
srcs = [
"nnapi_compatibility_lib_test.cc",
],
deps = [
":nnapi_compatibility_lib",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate_verbose_validation",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "nnapi_delegate_compatibility_checker_test",
srcs = [
"nnapi_delegate_compatibility_checker_test.cc",
],
data = [
"//tensorflow/lite:testdata/add.bin",
],
tags = ["no_oss"], #TODO(b/276295784): Re-enable when fixed.
deps = [
":nnapi_delegate_compatibility_checker",
"//tensorflow/core/platform:resource_loader",
"//tensorflow/lite:model_builder",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
],
)
@@ -0,0 +1,74 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_compatibility_lib.h"
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
namespace tflite {
namespace tools {
using ::tflite::delegate::nnapi::NNAPIValidationFailure;
TfLiteStatus CheckCompatibility(
TfLiteContext* context, int32_t runtime_feature_level,
std::vector<int>* supported_nodes,
std::map<int, std::vector<NNAPIValidationFailure>>* failures_by_node) {
if (!context) {
TFLITE_LOG_PROD_ONCE(TFLITE_LOG_ERROR, "Context is nullptr.");
return kTfLiteError;
}
// Gets execution plan.
TfLiteIntArray* execution_plan;
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
// Validates compatibility for each node.
for (int node_index : TfLiteIntArrayView(execution_plan)) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "Node index: %d", node_index);
TfLiteNode* node;
TfLiteRegistration* registration;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(
context, node_index, &node, &registration));
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures;
if (NNAPIDelegateKernel::Validate(
context, registration, runtime_feature_level, node,
/* is_accelerator_specified= */ true,
/* vendor_plugin= */ nullptr, &map_failures)) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "Built-in Code: %d",
registration->builtin_code);
if (supported_nodes) {
supported_nodes->push_back(node_index);
}
} else {
if (failures_by_node) {
(*failures_by_node)[node_index] = std::move(map_failures);
}
}
}
return kTfLiteOk;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,106 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
#include <cstdint>
#include <map>
#include <vector>
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
// WARNING: this header file is DEPRECATED.
// See https://developer.android.com/ndk/guides/neuralnetworks/migration-guide.
namespace tflite {
namespace tools {
// Check if the given TFLite flatbuffer model is compatible with NNAPI delegate.
// WARNING: This is an experimental API and subject to change.
TfLiteStatus CheckCompatibility(
TfLiteContext* context, int32_t runtime_feature_level,
std::vector<int>* supported_nodes,
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>*
failures_by_node);
// This utility delegate is required because some TfLiteContext related
// functions are forbidden if not calling in delegate.
// WARNING: This is an experimental class and subject to change.
class CompatibilityCheckerDelegate : public TfLiteDelegate {
public:
explicit CompatibilityCheckerDelegate(int32_t runtime_feature_level)
: TfLiteDelegate(TfLiteDelegateCreate()),
runtime_feature_level_(runtime_feature_level),
supported_nodes_(),
failures_by_node_() {
Prepare = DoPrepare;
CopyFromBufferHandle = DoCopyFromBufferHandle;
CopyToBufferHandle = DoCopyToBufferHandle;
FreeBufferHandle = DoFreeBufferHandle;
data_ = &delegate_data_;
}
std::vector<int> GetSupportedNodes() { return supported_nodes_; }
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>
GetFailuresByNode() {
return failures_by_node_;
}
protected:
static TfLiteStatus DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate) {
auto self = reinterpret_cast<CompatibilityCheckerDelegate*>(delegate);
TF_LITE_ENSURE_OK(context,
CheckCompatibility(context, self->runtime_feature_level_,
&(self->supported_nodes_),
&(self->failures_by_node_)));
return kTfLiteOk;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// There is no buffer handle in this delegate.
static void DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle* handle) {}
private:
int delegate_data_;
int runtime_feature_level_;
std::vector<int> supported_nodes_;
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>
failures_by_node_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
@@ -0,0 +1,92 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_compatibility_lib.h"
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
namespace tools {
namespace {
void CompatibilityCheckerDelegateDelete(TfLiteDelegate* delegate) {
delete static_cast<CompatibilityCheckerDelegate*>(delegate);
}
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type,
std::unique_ptr<CompatibilityCheckerDelegate> checker_delegate) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
SetDelegate(
{checker_delegate.release(), CompatibilityCheckerDelegateDelete});
// Builds interpreter and applies delegate.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
} // namespace
TEST(NnapiDelegateCompabilityTest, InvalidInput) {
EXPECT_EQ(CheckCompatibility(nullptr, 0, nullptr, nullptr), kTfLiteError);
}
TEST(NnapiDelegateCompabilityTest, CompatibleModel) {
auto checker_delegate = std::make_unique<CompatibilityCheckerDelegate>(
tflite::delegate::nnapi::kMinSdkVersionForNNAPI13);
auto* checker_delegate_ptr = checker_delegate.get();
AddOpModel add_op_model({TensorType_FLOAT32, {1, 2, 2, 1}},
{TensorType_FLOAT32, {1, 2, 2, 1}},
{TensorType_FLOAT32, {}}, ActivationFunctionType_NONE,
std::move(checker_delegate));
EXPECT_EQ(checker_delegate_ptr->GetSupportedNodes().size(), 1);
EXPECT_EQ(checker_delegate_ptr->GetFailuresByNode().size(), 0);
}
TEST(NnapiDelegateCompabilityTest, IncompatibleModel) {
auto checker_delegate = std::make_unique<CompatibilityCheckerDelegate>(
tflite::delegate::nnapi::kMinSdkVersionForNNAPI13);
auto* checker_delegate_ptr = checker_delegate.get();
// No activation function is supported for INT32 tensor type.
AddOpModel add_op_model(
{TensorType_INT32, {1, 2, 2, 1}}, {TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_RELU_N1_TO_1,
std::move(checker_delegate));
EXPECT_EQ(checker_delegate_ptr->GetSupportedNodes().size(), 0);
EXPECT_EQ(checker_delegate_ptr->GetFailuresByNode().size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,278 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_delegate_compatibility_checker.h"
#include <cctype>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "tensorflow/lite/core/interpreter_builder.h"
#include "tensorflow/lite/core/kernels/register.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/nnapi/NeuralNetworksTypes.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_util.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/online_helper_delegate.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
namespace {
// Gets canonical feature level enum value from the number. In case the
// feature level is not between 1 and 8, the default value (8) would be
// assigned.
void getCanonicalFeatureLevel(int runtime_feature_level,
int& canonical_feature_level) {
switch (runtime_feature_level) {
case 1:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_1;
break;
case 2:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_2;
break;
case 3:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_3;
break;
case 4:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_4;
break;
case 5:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_5;
break;
case 6:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_6;
break;
case 7:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_7;
break;
case 8:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_8;
break;
default:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_8;
}
}
// Checks if the input string is a valid feature level. To be a valid feature
// level, the string passed as a parameter should contain only one digit
// between 1 and 8.
absl::Status IsValidFeatureLevelInt(const std::string& s) {
if (s.size() == 1 && std::isdigit(s[0]) && s[0] > '0' && s[0] < '9') {
return absl::OkStatus();
}
return absl::InvalidArgumentError("Invalid runtime feature level.");
}
// Gets the runtime feature level from the configurations and convert its
// value to an integer.
absl::Status extractRuntimeFeatureLevel(
const std::unordered_map<std::string, std::string>& dcc_configs,
int& runtime_feature_level) {
std::string str_runtime_feature_level;
if (dcc_configs.find("nnapi-runtime_feature_level") == dcc_configs.end()) {
for (const auto& dcc_config : dcc_configs) {
// If an NNAPI parameter is set, but spelled incorrectly, return an error.
if (absl::StrContains(dcc_config.first, "nnapi")) {
return absl::InvalidArgumentError(
"The correct flag name is 'nnapi-runtime_feature_level");
}
}
// Use default as no NNAPI parameter is set.
str_runtime_feature_level =
std::to_string(tools::kDefaultRuntimeFeatureLevel);
} else {
str_runtime_feature_level = dcc_configs.at("nnapi-runtime_feature_level");
RETURN_IF_ERROR(IsValidFeatureLevelInt(str_runtime_feature_level));
}
runtime_feature_level = std::stoi(str_runtime_feature_level);
return absl::OkStatus();
}
// Converts NnapiValidationFailureType to compatibilityFailureType to store
// the error in op_result.
absl::Status convertToCompatibilityFailureType(
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures,
proto::OpCompatibilityResult* op_result) {
for (const auto& status : map_failures) {
auto compatibility_failure = op_result->add_compatibility_failures();
compatibility_failure->set_description(status.message);
switch (status.type) {
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperator:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedAndroidVersion:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_VERSION);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperatorVersion:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR_VERSION);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedInputType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_INPUT_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kNotRestrictedScaleCompliant:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_NOT_RESTRICTED_SCALE_COMPLIANT);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOutputType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OUTPUT_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperandSize:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_SIZE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperandValue:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_VALUE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedHybridOperator:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_HYBRID_OPERATOR);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedQuantizationType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_QUANTIZATION_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::kMissingRequiredOperand:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_MISSING_REQUIRED_OPERAND);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperandRank:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_RANK);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kInputTensorShouldHaveConstantShape:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_INPUT_TENSOR_SHOULD_HAVE_CONSTANT_SHAPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperatorVariant:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR_VARIANT);
break;
case delegate::nnapi::NNAPIValidationFailureType::kNoActivationExpected:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_NO_ACTIVATION_EXPECTED);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedQuantizationParameters:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_UNSUPPORTED_QUANTIZATION_PARAMETERS);
break;
default:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
compatibility_failure->set_description(
"Unknown validation failure type.");
}
}
return absl::OkStatus();
}
} // namespace
absl::Status
tools::NnapiDelegateCompatibilityChecker::checkOpCompatibilityOnline(
TfLiteContext* context, const TfLiteNode* node,
const TfLiteRegistration* registration,
std::unordered_map<std::string, std::string> dcc_configs,
tflite::proto::OpCompatibilityResult* op_result) {
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures;
int runtime_feature_level;
RETURN_IF_ERROR(
extractRuntimeFeatureLevel(dcc_configs, runtime_feature_level));
getCanonicalFeatureLevel(runtime_feature_level, runtime_feature_level);
if (NNAPIDelegateKernel::Validate(
context, registration, runtime_feature_level, node,
/* is_accelerator_specified= */ true,
/* vendor_plugin= */ nullptr, &map_failures)) {
op_result->set_is_supported(true);
} else {
RETURN_IF_ERROR(convertToCompatibilityFailureType(map_failures, op_result));
op_result->set_is_supported(false);
}
return absl::OkStatus();
}
std::unordered_map<std::string, std::string>
tools::NnapiDelegateCompatibilityChecker::getDccConfigurations() {
std::unordered_map<std::string, std::string> dcc_configs;
dcc_configs["nnapi-runtime_feature_level"] =
std::to_string(runtime_feature_level_);
return dcc_configs;
}
absl::Status tools::NnapiDelegateCompatibilityChecker::setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) {
RETURN_IF_ERROR(
extractRuntimeFeatureLevel(dcc_configs, runtime_feature_level_));
return absl::OkStatus();
}
absl::Status
tools::NnapiDelegateCompatibilityChecker::checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) {
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder interpreter_builder(*model_buffer, resolver);
auto dcc_configs = getDccConfigurations();
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>,
proto::OpCompatibilityResult*)>
check_op_func_ptr = &checkOpCompatibilityOnline;
OnlineHelperDelegate delegate(dcc_configs, check_op_func_ptr, result);
interpreter_builder.AddDelegate(&delegate);
interpreter_builder(&interpreter);
return absl::OkStatus();
}
// TODO(b/243489631): Implement the function.
absl::Status tools::NnapiDelegateCompatibilityChecker::checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) {
return absl::UnimplementedError(
"Offline mode is not yet supported on NNAPI delegate compatibility "
"checker.");
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,92 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
// WARNING: this header file is DEPRECATED.
// See https://developer.android.com/ndk/guides/neuralnetworks/migration-guide.
namespace tflite {
namespace tools {
// Default runtime feature level used when no parameters are specified.
inline constexpr int kDefaultRuntimeFeatureLevel = 8;
// Class to check if an operation or a model is compatible with NNAPI delegate.
// Supported parameters:
// - nnapi-runtime_feature_level: Between 1 and 8 (default value: 8)
class NnapiDelegateCompatibilityChecker
: public DelegateCompatibilityCheckerBase {
public:
NnapiDelegateCompatibilityChecker() {
runtime_feature_level_ = kDefaultRuntimeFeatureLevel;
}
absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) override;
// Checks if the node is compatible with the NNAPI delegate using online mode.
// Params:
// context: Used to get the tensors. TfLiteTensors can be obtained via
// TfLiteContext, which are used to get tensor type and tensor data,
// the same way as with OpSignature in Offline mode.
// node: Used with context to get the desired tensor, e.g.:
// context->tensors[node->inputs->data[0]]
// registration: Used to get the builtin code and the operator version.
// op_result: Used to store if the node is compatible with the delegate or
// not and why (with a human readable message).
static absl::Status checkOpCompatibilityOnline(
TfLiteContext* context, const TfLiteNode* node,
const TfLiteRegistration* registration,
std::unordered_map<std::string, std::string> dcc_configs,
tflite::proto::OpCompatibilityResult* op_result);
// Returns a dictionary with NNAPI delegate specific params.
// Keys:
// - nnapi-runtime_feature_level
std::unordered_map<std::string, std::string> getDccConfigurations() override;
// Sets the parameters needed in the specific DCC.
// Keys:
// - nnapi-runtime_feature_level
absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) override;
private:
absl::Status checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) override;
// Runtime feature level
// Refer to '/tensorflow/lite/nnapi/NeuralNetworksTypes.h'
int runtime_feature_level_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
@@ -0,0 +1,184 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_delegate_compatibility_checker.h"
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "tensorflow/core/platform/resource_loader.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok());
#endif
namespace {
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
// Builds interpreter.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
// Class to test the NNAPI delegate compatibility checker (DCC)
class NnapiDccTest : public ::testing::Test {
protected:
void SetUp() override {}
void TearDown() override { compatibility_result_.Clear(); }
NnapiDelegateCompatibilityChecker nnapi_dcc_;
proto::CompatibilityResult compatibility_result_;
};
} // namespace
TEST_F(NnapiDccTest, ValidRuntimeFeatureLevel) {
std::unordered_map dcc_configs = nnapi_dcc_.getDccConfigurations();
EXPECT_EQ(dcc_configs["nnapi-runtime_feature_level"], "8");
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs["nnapi-runtime_feature_level"] = "1";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs["nnapi-runtime_feature_level"] = "8";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs.clear();
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
EXPECT_EQ(nnapi_dcc_.getDccConfigurations()["nnapi-runtime_feature_level"],
"8");
}
TEST_F(NnapiDccTest, InvalidRuntimeFeatureLevel) {
std::unordered_map dcc_configs = nnapi_dcc_.getDccConfigurations();
dcc_configs["nnapi-runtime_feature_level"] = "03";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "a";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "28123497123489123841212344516";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "30.0";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "-30";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "9";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs.clear();
dcc_configs["nnapi-runtim_feature_level"] = "8";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
}
TEST_F(NnapiDccTest, CompatibleModelOnlineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
auto model = fb_model->GetModel();
EXPECT_EQ(model->subgraphs()->size(), 1);
EXPECT_EQ(model->subgraphs()->Get(0)->operators()->size(), 2);
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_TRUE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 2);
}
TEST_F(NnapiDccTest, IncompatibleModelOperation) {
// No activation function is supported for INT32 tensor type.
AddOpModel add_op_model(
{TensorType_INT32, {1, 2, 2, 1}}, {TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_RELU_N1_TO_1);
auto fb_model = tflite::FlatBufferModel::BuildFromModel(
tflite::GetModel(add_op_model.GetModelBuffer()));
ASSERT_TRUE(fb_model);
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 1);
}
TEST_F(NnapiDccTest, IncompatibleModelFeatureLevel) {
// INT32 input tensor type is not supported if runtime feature level < 4.
AddOpModel add_op_model({TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_NONE);
auto fb_model = tflite::FlatBufferModel::BuildFromModel(
tflite::GetModel(add_op_model.GetModelBuffer()));
ASSERT_TRUE(fb_model);
auto nnapi_configs = nnapi_dcc_.getDccConfigurations();
nnapi_configs["nnapi-runtime_feature_level"] = "2";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(nnapi_configs));
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,17 @@
load(
"//tensorflow/core/platform:build_config.bzl",
"tf_proto_library",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
tf_proto_library(
name = "compatibility_result",
srcs = [
"compatibility_result.proto",
],
)
@@ -0,0 +1,138 @@
// Copyright 2022 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.
syntax = "proto2";
package tflite.proto;
enum CompatibilityFailureType {
// Quantization scale and/or zero point are not in the supported
// value(s) for the accelerated operation.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_QUANTIZATION_PARAMETERS = 0;
// Indicates that the caller specified an invalid argument, such as
// incorrect stride values.
// Applied DDC(s): GPU
DCC_INVALID_ARGUMENT = 1;
// Indicates an internal error has occurred and some invariants
// expected by the underlying system have not been satisfied, such as
// expecting different number of input or ouput tensors.
// Applied DDC(s): GPU
DCC_INTERNAL_ERROR = 2;
// Indicates the operation is not implemented or supported in this
// service. In this case, the operation should not be re-attempted.
// Applied DDC(s): GPU
DCC_UNIMPLEMENTED_ERROR = 3;
// Indicates the operation was attempted past the valid range, such
// as requesting an index that goes beyond the array size.
// Applied DDC(s): GPU
DCC_OUT_OF_RANGE = 4;
// The operator is not supported by the Delegate.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR = 5;
// The given operation or operands are not supported on the
// specified runtime feature level. The min supported version is specified in
// the compatibility failure message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_VERSION = 6;
// The version of the operator (value of OpSignature.version)
// for the given op is not supported. The max supported version
// is specified in the compatibility failure message.
// For more details on each operator version see
// the GetBuiltinOperatorVersion function in
// tensorflow/lite/tools/versioning/op_version.cc.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR_VERSION = 7;
// The given input operand type is not supported for the current
// combination of operator type and runtime feature level.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_INPUT_TYPE = 8;
// When using NN API version 1.0 or 1.1, the condition
// input_scale * filter_scale < output_scale
// must be true for quantized versions of the following ops:
// * CONV_2D
// * DEPTHWISE_CONV_2D
// * FULLY_CONNECTED (where filter actually stands for weights)
// The condition is relaxed and no longer required since version 1.2.
// Applied DDC(s): NNAPI
DCC_NOT_RESTRICTED_SCALE_COMPLIANT = 9;
// The given output operand type is not supported for the current
// combination of operator type and runtime feature level.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OUTPUT_TYPE = 10;
// The size of the operand tensor is too large.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_SIZE = 11;
// The value of one of the operands or of a combination of operands
// is not supported. Details are provided in the compatibility failure
// message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_VALUE = 12;
// The combination of float inputs and quantized weights or filters
// is not supported.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_HYBRID_OPERATOR = 13;
// The quantization type (for example per-channel quantization) is
// not supported.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_QUANTIZATION_TYPE = 14;
// The accelerated version of operation requires a specific operand
// to be specified.
// Applied DDC(s): NNAPI
DCC_MISSING_REQUIRED_OPERAND = 15;
// The rank of the operand is not supported. Details in the
// compatibility failure message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_RANK = 16;
// The input tensor cannot be dynamically-sized.
// Applied DDC(s): NNAPI
DCC_INPUT_TENSOR_SHOULD_HAVE_CONSTANT_SHAPE = 17;
// The operator has a different number of inputs of the one or ones
// that are supported by NNAPI.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR_VARIANT = 18;
// The accelerated version of the operator cannot specify an
// activation function.
// Applied DDC(s): NNAPI
DCC_NO_ACTIVATION_EXPECTED = 19;
}
// Indicates the type and a human readable text for an error in an operation.
message CompatibilityFailure {
// Type of the errors.
optional CompatibilityFailureType failure_type = 1;
// Human readable message explaining the error.
optional string description = 2;
}
// Result for one operation of the given model and stores if the operation
// is supported. If it is supported, validation_failures will not have a value.
// If it is not supported, validation_failures will contain all the errors for
// that operation. Also saves the subgraph index inside the model and the
// operator index inside the subgraph.
message OpCompatibilityResult {
// True if the operation is supported for the required DCC.
optional bool is_supported = 1;
// Index of the subgraph where this operation is contained.
optional int32 subgraph_index_in_model = 2;
// Index of the operator inside the subgraph.
optional int32 operator_index_in_subgraph = 3;
// Type of the errors.
repeated CompatibilityFailure compatibility_failures = 4;
}
message CompatibilityResult {
// One result for each operation.
repeated OpCompatibilityResult compatibility_results = 1;
}
@@ -0,0 +1,120 @@
/* 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 <utility>
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#if defined(__APPLE__)
#include "TargetConditionals.h"
#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) || \
(TARGET_OS_OSX && TARGET_CPU_ARM64)
// Only enable coreml delegate when using a real iPhone device or Apple Silicon.
#define REAL_IPHONE_DEVICE
#include "tensorflow/lite/delegates/coreml/coreml_delegate.h"
#endif
#endif
namespace tflite {
namespace tools {
class CoreMlDelegateProvider : public DelegateProvider {
public:
CoreMlDelegateProvider() {
#if defined(REAL_IPHONE_DEVICE)
default_params_.AddParam("use_coreml", ToolParam::Create<bool>(false));
default_params_.AddParam("coreml_version", ToolParam::Create<int>(0));
#endif
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "COREML"; }
};
REGISTER_DELEGATE_PROVIDER(CoreMlDelegateProvider);
std::vector<Flag> CoreMlDelegateProvider::CreateFlags(
ToolParams* params) const {
#if defined(REAL_IPHONE_DEVICE)
std::vector<Flag> flags = {
CreateFlag<bool>("use_coreml", params, "use Core ML"),
CreateFlag<int>("coreml_version", params,
"Target Core ML version for model conversion. "
"The default value is 0 and it means using the newest "
"version that's available on the device."),
};
return flags;
#else
return {};
#endif
}
void CoreMlDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
#if defined(REAL_IPHONE_DEVICE)
LOG_TOOL_PARAM(params, bool, "use_coreml", "Use CoreML", verbose);
LOG_TOOL_PARAM(params, int, "coreml_version", "CoreML version", verbose);
#endif
}
TfLiteDelegatePtr CoreMlDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
TfLiteDelegatePtr delegate = CreateNullDelegate();
#if defined(REAL_IPHONE_DEVICE)
if (params.Get<bool>("use_coreml")) {
TfLiteCoreMlDelegateOptions coreml_opts = {
.enabled_devices = TfLiteCoreMlDelegateAllDevices};
coreml_opts.coreml_version = params.Get<int>("coreml_version");
coreml_opts.max_delegated_partitions =
params.Get<int>("max_delegated_partitions");
coreml_opts.min_nodes_per_partition =
params.Get<int>("min_nodes_per_partition");
#ifdef TFLITE_DEBUG_DELEGATE
coreml_opts.first_delegate_node_index =
params.Get<int>("first_delegate_node_index");
coreml_opts.last_delegate_node_index =
params.Get<int>("last_delegate_node_index");
#endif // TFLITE_DEBUG_DELEGATE
delegate = TfLiteDelegatePtr(TfLiteCoreMlDelegateCreate(&coreml_opts),
&TfLiteCoreMlDelegateDelete);
if (!delegate) {
TFLITE_LOG(WARN)
<< "CoreML acceleration is unsupported on this platform.";
}
}
#endif
return delegate;
}
std::pair<TfLiteDelegatePtr, int>
CoreMlDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
int rank = 0;
#if defined(REAL_IPHONE_DEVICE)
rank = params.GetPosition<bool>("use_coreml");
#endif
return std::make_pair(std::move(ptr), rank);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,140 @@
/* 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 <cstdint>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
namespace tflite {
namespace tools {
// This class actually doesn't provide any TFLite delegate instances, it simply
// provides common params and flags that are common to all actual delegate
// providers.
class DefaultExecutionProvider : public DelegateProvider {
public:
DefaultExecutionProvider() {
default_params_.AddParam("help", ToolParam::Create<bool>(false));
default_params_.AddParam("num_threads", ToolParam::Create<int32_t>(-1));
default_params_.AddParam("max_delegated_partitions",
ToolParam::Create<int32_t>(0));
default_params_.AddParam("min_nodes_per_partition",
ToolParam::Create<int32_t>(0));
default_params_.AddParam("delegate_serialize_dir",
ToolParam::Create<std::string>(""));
default_params_.AddParam("delegate_serialize_token",
ToolParam::Create<std::string>(""));
default_params_.AddParam("first_delegate_node_index",
ToolParam::Create<int32_t>(0));
default_params_.AddParam(
"last_delegate_node_index",
ToolParam::Create<int32_t>(std::numeric_limits<int32_t>::max()));
default_params_.AddParam("gpu_invoke_loop_times",
ToolParam::Create<int32_t>(-1));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "Default-NoDelegate"; }
};
REGISTER_DELEGATE_PROVIDER(DefaultExecutionProvider);
std::vector<Flag> DefaultExecutionProvider::CreateFlags(
ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<bool>("help", params,
"Print out all supported flags if true."),
CreateFlag<int32_t>("num_threads", params,
"number of threads used for inference on CPU."),
CreateFlag<int32_t>("max_delegated_partitions", params,
"Max number of partitions to be delegated."),
CreateFlag<int32_t>(
"min_nodes_per_partition", params,
"The minimal number of TFLite graph nodes of a partition that has to "
"be reached for it to be delegated.A negative value or 0 means to "
"use the default choice of each delegate."),
CreateFlag<int32_t>(
"first_delegate_node_index", params,
"The index of the first node that could be delegated. Used only when "
"TFLITE_DEBUG_DELEGATE is defined. Default is 0."),
CreateFlag<int32_t>(
"last_delegate_node_index", params,
"The index of the last node that could be delegated. Used only when "
"TFLITE_DEBUG_DELEGATE is defined. Default is INT_MAX."),
CreateFlag<int32_t>(
"gpu_invoke_loop_times", params,
"Number of GPU delegate invoke loop iterations. Used only when "
"TFLITE_GPU_ENABLE_INVOKE_LOOP is defined. Default is 1."),
CreateFlag<std::string>(
"delegate_serialize_dir", params,
"Directory to be used by delegates for serializing any model data. "
"This allows the delegate to save data into this directory to reduce "
"init time after the first run. Currently supported by NNAPI "
"delegate with specific backends on Android. Note that "
"delegate_serialize_token is also required to enable this feature."),
CreateFlag<std::string>(
"delegate_serialize_token", params,
"Model-specific token acting as a namespace for delegate "
"serialization. Unique tokens ensure that the delegate doesn't read "
"inapplicable/invalid data. Note that delegate_serialize_dir is also "
"required to enable this feature."),
};
return flags;
}
void DefaultExecutionProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, bool, "help", "print out all supported flags",
verbose);
LOG_TOOL_PARAM(params, int32_t, "num_threads",
"#threads used for CPU inference", verbose);
LOG_TOOL_PARAM(params, int32_t, "max_delegated_partitions",
"Max number of delegated partitions", verbose);
LOG_TOOL_PARAM(params, int32_t, "min_nodes_per_partition",
"Min nodes per partition", verbose);
LOG_TOOL_PARAM(params, int32_t, "first_delegate_node_index",
"Index of the first node that could be delegated", verbose);
LOG_TOOL_PARAM(params, int32_t, "last_delegate_node_index",
"Index of the last node that could be delegated", verbose);
LOG_TOOL_PARAM(params, int32_t, "gpu_invoke_loop_times",
"Number of GPU delegate invoke loop iterations", verbose);
LOG_TOOL_PARAM(params, std::string, "delegate_serialize_dir",
"Directory for delegate serialization", verbose);
LOG_TOOL_PARAM(params, std::string, "delegate_serialize_token",
"Model-specific token/key for delegate serialization.",
verbose);
}
TfLiteDelegatePtr DefaultExecutionProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
return CreateNullDelegate();
}
std::pair<TfLiteDelegatePtr, int>
DefaultExecutionProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr), 0);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,89 @@
/* Copyright 2021 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/tools/delegates/delegate_provider.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
namespace tflite {
namespace tools {
TfLiteDelegatePtr CreateNullDelegate() {
return TfLiteDelegatePtr(nullptr, [](TfLiteOpaqueDelegate*) {});
}
void ProvidedDelegateList::AddAllDelegateParams() const {
for (const auto& provider : providers_) {
params_->Merge(provider->DefaultParams());
}
}
void ProvidedDelegateList::AppendCmdlineFlags(std::vector<Flag>& flags) const {
for (const auto& provider : providers_) {
auto delegate_flags = provider->CreateFlags(params_);
flags.insert(flags.end(), delegate_flags.begin(), delegate_flags.end());
}
}
void ProvidedDelegateList::RemoveCmdlineFlag(std::vector<Flag>& flags,
const std::string& name) const {
decltype(flags.begin()) it;
for (it = flags.begin(); it < flags.end();) {
if (it->GetFlagName() == name) {
it = flags.erase(it);
} else {
++it;
}
}
}
std::vector<ProvidedDelegateList::ProvidedDelegate>
ProvidedDelegateList::CreateAllRankedDelegates(const ToolParams& params) const {
std::vector<ProvidedDelegateList::ProvidedDelegate> delegates;
for (const auto& provider : providers_) {
auto ptr_rank = provider->CreateRankedTfLiteDelegate(params);
// It's possible that a delegate of certain type won't be created as
// user-specified tool params tells not to.
if (ptr_rank.first == nullptr) continue;
static bool already_logged = false;
if (!already_logged) {
TFLITE_LOG(INFO) << provider->GetName() << " delegate created.";
#ifndef NDEBUG
provider->LogParams(params, /*verbose=*/false);
#endif
already_logged = true;
}
ProvidedDelegateList::ProvidedDelegate info;
info.provider = provider.get();
info.delegate = std::move(ptr_rank.first);
info.rank = ptr_rank.second;
delegates.emplace_back(std::move(info));
}
std::sort(delegates.begin(), delegates.end(),
[](const ProvidedDelegateList::ProvidedDelegate& a,
const ProvidedDelegateList::ProvidedDelegate& b) {
return a.rank < b.rank;
});
return delegates;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,177 @@
/* 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_TOOLS_DELEGATES_DELEGATE_PROVIDER_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_DELEGATE_PROVIDER_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/tools/command_line_flags.h"
#include "tensorflow/lite/tools/logging.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
// Same w/ Interpreter::TfLiteDelegatePtr to avoid pulling
// tensorflow/lite/interpreter.h dependency
using TfLiteDelegatePtr =
std::unique_ptr<TfLiteOpaqueDelegate, void (*)(TfLiteOpaqueDelegate*)>;
class DelegateProvider {
public:
virtual ~DelegateProvider() {}
// Create a list of command-line parsable flags based on tool params inside
// 'params' whose value will be set to the corresponding runtime flag value.
virtual std::vector<Flag> CreateFlags(ToolParams* params) const = 0;
// Log tool params. If 'verbose' is set to false, the param is going to be
// only logged if its value has been set, say via being parsed from
// commandline flags.
virtual void LogParams(const ToolParams& params, bool verbose) const = 0;
// Create a TfLiteDelegate based on tool params.
virtual TfLiteDelegatePtr CreateTfLiteDelegate(
const ToolParams& params) const = 0;
// Similar to the above, create a TfLiteDelegate based on tool params. If the
// same set of tool params could lead to creating multiple TfLite delegates,
// also return a relative rank of the delegate that indicates the order of the
// returned delegate that should be applied to the TfLite runtime.
virtual std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const = 0;
virtual std::string GetName() const = 0;
const ToolParams& DefaultParams() const { return default_params_; }
protected:
template <typename T>
Flag CreateFlag(const char* name, ToolParams* params,
const std::string& usage) const {
return Flag(
name,
[params, name](const T& val, int argv_position) {
params->Set<T>(name, val, argv_position);
},
default_params_.Get<T>(name), usage, Flag::kOptional);
}
ToolParams default_params_;
};
using DelegateProviderPtr = std::unique_ptr<DelegateProvider>;
using DelegateProviderList = std::vector<DelegateProviderPtr>;
class DelegateProviderRegistrar {
public:
template <typename T>
struct Register {
Register() {
auto* const instance = DelegateProviderRegistrar::GetSingleton();
instance->providers_.emplace_back(DelegateProviderPtr(new T()));
}
};
static const DelegateProviderList& GetProviders() {
return GetSingleton()->providers_;
}
private:
DelegateProviderRegistrar() {}
DelegateProviderRegistrar(const DelegateProviderRegistrar&) = delete;
DelegateProviderRegistrar& operator=(const DelegateProviderRegistrar&) =
delete;
static DelegateProviderRegistrar* GetSingleton() {
static auto* instance = new DelegateProviderRegistrar();
return instance;
}
DelegateProviderList providers_;
};
#define REGISTER_DELEGATE_PROVIDER_VNAME(T) gDelegateProvider_##T##_
#define REGISTER_DELEGATE_PROVIDER(T) \
static tflite::tools::DelegateProviderRegistrar::Register<T> \
REGISTER_DELEGATE_PROVIDER_VNAME(T);
// Creates a null delegate, useful for cases where no reasonable delegate can be
// created.
TfLiteDelegatePtr CreateNullDelegate();
// A global helper function to get all registered delegate providers.
inline const DelegateProviderList& GetRegisteredDelegateProviders() {
return DelegateProviderRegistrar::GetProviders();
}
// A helper class to create a list of TfLite delegates based on the provided
// ToolParams and the global DelegateProviderRegistrar.
class ProvidedDelegateList {
public:
struct ProvidedDelegate {
ProvidedDelegate()
: provider(nullptr), delegate(CreateNullDelegate()), rank(0) {}
const DelegateProvider* provider;
TfLiteDelegatePtr delegate;
int rank;
};
ProvidedDelegateList() : ProvidedDelegateList(/*params*/ nullptr) {}
// 'params' is the ToolParams instance that this class will operate on,
// including adding all registered delegate parameters to it etc.
explicit ProvidedDelegateList(ToolParams* params)
: providers_(GetRegisteredDelegateProviders()), params_(params) {}
const DelegateProviderList& providers() const { return providers_; }
// Add all registered delegate params to the contained 'params_'.
void AddAllDelegateParams() const;
// Append command-line parsable flags to 'flags' of all registered delegate
// providers, and associate the flag values at runtime with the contained
// 'params_'.
void AppendCmdlineFlags(std::vector<Flag>& flags) const;
// Removes command-line parsable flag 'name' from 'flags'
void RemoveCmdlineFlag(std::vector<Flag>& flags,
const std::string& name) const;
// Return a list of TfLite delegates based on the provided 'params', and the
// list has been already sorted in ascending order according to the rank of
// the particular parameter that enables the creation of the delegate.
std::vector<ProvidedDelegate> CreateAllRankedDelegates(
const ToolParams& params) const;
// Similar to the above, the list of TfLite delegates are created based on the
// contained 'params_'.
std::vector<ProvidedDelegate> CreateAllRankedDelegates() const {
return CreateAllRankedDelegates(*params_);
}
private:
const DelegateProviderList& providers_;
// Represent the set of "ToolParam"s that this helper class will operate on.
ToolParams* const params_; // Not own the memory.
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_DELEGATE_PROVIDER_H_
@@ -0,0 +1,92 @@
/* Copyright 2021 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/tools/delegates/delegate_provider.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/c/test_util.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
namespace {
TEST(ProvidedDelegateListTest, AddAllDelegateParams) {
ToolParams params;
ProvidedDelegateList providers(&params);
providers.AddAllDelegateParams();
// As we link the test with XNNPACK and nnapi delegate providers, we should
// expect to have these two knob parameters.
EXPECT_TRUE(params.HasParam("use_xnnpack"));
// TODO(b/249485631): Enable the check after NNAPI Delegate Provider supports
// stable TFLite ABI.
#if !TFLITE_WITH_STABLE_ABI
EXPECT_TRUE(params.HasParam("use_nnapi"));
#endif // !TFLITE_WITH_STABLE_ABI
}
TEST(ProvidedDelegateListTest, AppendCmdlineFlags) {
std::vector<Flag> flags;
ToolParams params;
ProvidedDelegateList providers(&params);
providers.AddAllDelegateParams();
providers.AppendCmdlineFlags(flags);
EXPECT_FALSE(flags.empty());
}
TEST(KernelTestDelegateProvidersTest, CreateAllRankedDelegates) {
#if !defined(__Fuchsia__) && !defined(__s390x__) && \
!defined(TFLITE_WITHOUT_XNNPACK)
ToolParams params;
ProvidedDelegateList providers(&params);
providers.AddAllDelegateParams();
// TODO(b/249054271): Dummy delegate hasn't been migrated to use TFLite with
// stable ABI yet. The check here can be removed after the extension.
#if TFLITE_WITH_STABLE_ABI
ASSERT_EQ(TfLiteInitializeShimsForTest(), 0);
params.Set<bool>("use_xnnpack", true, 1);
auto delegates = providers.CreateAllRankedDelegates();
EXPECT_EQ(1, delegates.size());
EXPECT_EQ("XNNPACK", delegates.front().provider->GetName());
EXPECT_NE(nullptr, delegates.front().delegate.get());
EXPECT_EQ(1, delegates.front().rank);
#else // TFLITE_WITH_STABLE_ABI
// We set the position of "use_xnnpack" to be smaller than that of
// "use_dummy_delegate" so that the Dummy delegate will be ahead of the
// XNNPACK delegate in the returned list.
params.Set<bool>("use_xnnpack", true, 2);
params.Set<bool>("use_dummy_delegate", true, 1);
auto delegates = providers.CreateAllRankedDelegates();
EXPECT_EQ(2, delegates.size());
EXPECT_EQ("DummyDelegate", delegates.front().provider->GetName());
EXPECT_EQ(1, delegates.front().rank);
EXPECT_NE(nullptr, delegates.front().delegate.get());
EXPECT_EQ("XNNPACK", delegates.back().provider->GetName());
EXPECT_NE(nullptr, delegates.back().delegate.get());
EXPECT_EQ(2, delegates.back().rank);
#endif // TFLITE_WITH_STABLE_ABI
#endif
}
} // namespace
} // namespace tools
} // namespace tflite
@@ -0,0 +1,70 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
# Provides stable ABI delegate.
load("//tensorflow/lite:build_def.bzl", "tflite_copts")
# copybara:uncomment package(default_applicable_licenses = ["//tensorflow:LICENSE"])
cc_library(
name = "delegate_provider",
srcs = [
"stable_delegate_provider.cc",
],
copts = tflite_copts(),
visibility = ["//visibility:public"],
deps = [
"//tensorflow/lite/c:common",
"//tensorflow/lite/tools:command_line_flags",
"//tensorflow/lite/tools:logging",
"//tensorflow/lite/tools:tool_params",
"//tensorflow/lite/tools/delegates:delegate_provider_hdr",
] + select({
# Stable delegate does not support Windows because the shared library loader hasn't been
# extended to support Windows.
"//tensorflow:windows": [],
"//conditions:default": [
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/acceleration/configuration/c:delegate_plugin",
"//tensorflow/lite/acceleration/configuration/c:stable_delegate",
"//tensorflow/lite/delegates/utils/experimental/stable_delegate:delegate_loader",
"//tensorflow/lite/delegates/utils/experimental/stable_delegate:tflite_settings_json_parser",
],
}),
# Statically registers itself with DelegateProviderRegistrar.
alwayslink = 1,
)
cc_test(
name = "delegate_provider_test",
size = "small",
srcs = ["stable_delegate_provider_test.cc"],
data = [
":test_invalid_settings.json",
":test_missing_delegate_path_settings.json",
":test_missing_stable_delegate_settings.json",
":test_sample_stable_delegate_settings.json",
":test_stable_xnnpack_settings.json",
"//tensorflow/lite/delegates/utils/experimental/sample_stable_delegate:tensorflowlite_sample_stable_delegate",
"//tensorflow/lite/delegates/utils/experimental/stable_delegate:tensorflowlite_stable_xnnpack_delegate",
],
# Disable the test on Windows as the shared library loader doesn't support it.
tags = ["no-windows"],
deps = [
":delegate_provider",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"//tensorflow/lite/tools:tool_params",
"//tensorflow/lite/tools/delegates:delegate_provider_hdr",
"//tensorflow/lite/tools/delegates:delegate_provider_lib",
"@com_google_googletest//:gtest_main",
"@pthreadpool",
],
)
exports_files(
srcs = [
"test_invalid_settings.json",
"test_sample_stable_delegate_settings.json",
],
visibility = ["//visibility:public"],
)
@@ -0,0 +1,192 @@
/* 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 <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/tools/command_line_flags.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/logging.h"
#include "tensorflow/lite/tools/tool_params.h"
#if !defined(_WIN32)
#include "tensorflow/lite/acceleration/configuration/c/delegate_plugin.h"
#include "tensorflow/lite/acceleration/configuration/c/stable_delegate.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/delegates/utils/experimental/stable_delegate/delegate_loader.h"
#include "tensorflow/lite/delegates/utils/experimental/stable_delegate/tflite_settings_json_parser.h"
#endif // !defined(_WIN32)
namespace tflite {
namespace tools {
// The stable delegate provider is disabled on Windows as the delegate shared
// library loader doesn't support Windows platform.
#if !defined(_WIN32)
namespace {
// Parses the JSON settings, loads the appropriate stable delegate plugin,
// and uses the stable delegate plugin to create a stable delegate.
// This assumes that the settings in the given json_settings_file_path
// will not to change during the lifetime of the program.
TfLiteDelegatePtr CreateStableDelegate(
const std::string& json_settings_file_path);
// Class that encapulates the stable delegate cache management.
class StableDelegatePluginLoader {
public:
// Returns a singleton instance of this class.
static StableDelegatePluginLoader& GetInstance() {
static StableDelegatePluginLoader* const instance =
new StableDelegatePluginLoader;
return *instance;
}
// As per ::tflite::tools::CreateStableDelegate, above.
TfLiteDelegatePtr CreateStableDelegate(
const std::string& json_settings_file_path);
private:
struct CacheEntry {
const TfLiteStableDelegate* stable_delegate = nullptr;
delegates::utils::TfLiteSettingsJsonParser parser; // Owns parsed_settings.
const TFLiteSettings* parsed_settings = nullptr;
};
StableDelegatePluginLoader() = default;
const CacheEntry* LoadStableDelegatePlugin(
const std::string& json_settings_file_path);
std::map<std::string /*settings_file_path*/, CacheEntry> cache_;
};
const StableDelegatePluginLoader::CacheEntry*
StableDelegatePluginLoader::LoadStableDelegatePlugin(
const std::string& json_settings_file_path) {
auto it = cache_.find(json_settings_file_path);
if (it != cache_.end()) {
return &it->second;
}
CacheEntry result;
const TFLiteSettings* tflite_settings =
result.parser.Parse(json_settings_file_path);
result.parsed_settings = tflite_settings;
if (!tflite_settings || !tflite_settings->stable_delegate_loader_settings() ||
!tflite_settings->stable_delegate_loader_settings()->delegate_path()) {
TFLITE_LOG(ERROR) << "Invalid TFLiteSettings for the stable delegate.";
result.stable_delegate = nullptr;
} else {
std::string delegate_path =
tflite_settings->stable_delegate_loader_settings()
->delegate_path()
->str();
result.stable_delegate =
delegates::utils::LoadDelegateFromSharedLibrary(delegate_path);
if (!result.stable_delegate || !result.stable_delegate->delegate_plugin) {
TFLITE_LOG(ERROR) << "Failed to load stable ABI delegate from stable ABI "
"delegate binary ("
<< delegate_path << ").";
}
}
auto it2 = cache_.emplace(json_settings_file_path, std::move(result)).first;
return &it2->second;
}
TfLiteDelegatePtr CreateStableDelegate(
const std::string& json_settings_file_path) {
return StableDelegatePluginLoader::GetInstance().CreateStableDelegate(
json_settings_file_path);
}
TfLiteDelegatePtr StableDelegatePluginLoader::CreateStableDelegate(
const std::string& json_settings_file_path) {
if (json_settings_file_path.empty()) {
return CreateNullDelegate();
}
const CacheEntry* entry =
StableDelegatePluginLoader::GetInstance().LoadStableDelegatePlugin(
json_settings_file_path);
if (!entry || !entry->stable_delegate ||
!entry->stable_delegate->delegate_plugin) {
return CreateNullDelegate();
}
const TfLiteOpaqueDelegatePlugin* delegate_plugin =
entry->stable_delegate->delegate_plugin;
return TfLiteDelegatePtr(delegate_plugin->create(entry->parsed_settings),
delegate_plugin->destroy);
}
} // namespace
#endif // !defined(_WIN32)
class StableAbiDelegateProvider : public DelegateProvider {
public:
StableAbiDelegateProvider() {
default_params_.AddParam("stable_delegate_settings_file",
ToolParam::Create<std::string>(""));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "STABLE_DELEGATE"; }
};
REGISTER_DELEGATE_PROVIDER(StableAbiDelegateProvider);
std::vector<Flag> StableAbiDelegateProvider::CreateFlags(
ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<std::string>("stable_delegate_settings_file", params,
"The path to the delegate settings JSON file.")};
return flags;
}
void StableAbiDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
if (params.Get<std::string>("stable_delegate_settings_file").empty()) return;
LOG_TOOL_PARAM(params, std::string, "stable_delegate_settings_file",
"Delegate settings file path", verbose);
}
TfLiteDelegatePtr StableAbiDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
#if !defined(_WIN32)
std::string stable_delegate_settings_file =
params.Get<std::string>("stable_delegate_settings_file");
return CreateStableDelegate(stable_delegate_settings_file);
#else // !defined(_WIN32)
return CreateNullDelegate();
#endif // !defined(_WIN32)
}
std::pair<TfLiteDelegatePtr, int>
StableAbiDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr), params.GetPosition<std::string>(
"stable_delegate_settings_file"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,96 @@
/* Copyright 2022 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 <vector>
#include <gtest/gtest.h>
#include "pthreadpool.h" // from @pthreadpool
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
namespace {
static constexpr char kTestSettingsSrcDir[] =
"tensorflow/lite/tools/delegates/experimental/stable_delegate/";
static constexpr char kGoodStableDelegateSettings[] =
"test_sample_stable_delegate_settings.json";
static constexpr char kGoodXNNPackDelegateSettings[] =
"test_stable_xnnpack_settings.json";
static constexpr char kBadMissingFile[] = "missing.json";
static constexpr char kBadInvalidSettings[] = "test_invalid_settings.json";
static constexpr char kBadMissingStableDelegateSettings[] =
"test_missing_stable_delegate_settings.json";
static constexpr char kBadMissingDelegatePathSettings[] =
"test_missing_delegate_path_settings.json";
std::vector<ProvidedDelegateList::ProvidedDelegate> CreateDelegates(
const std::string& settings_file_path) {
ToolParams params;
ProvidedDelegateList providers(&params);
providers.AddAllDelegateParams();
params.Set<std::string>("stable_delegate_settings_file", settings_file_path,
/*position=*/1);
return providers.CreateAllRankedDelegates();
}
TEST(StableAbiDelegateProviderTest, CreateDelegate) {
auto delegates = CreateDelegates(std::string(kTestSettingsSrcDir) +
kGoodStableDelegateSettings);
// Only the stable ABI delegate is registered.
EXPECT_EQ(1, delegates.size());
EXPECT_EQ("STABLE_DELEGATE", delegates.front().provider->GetName());
EXPECT_NE(nullptr, delegates.front().delegate.get());
EXPECT_EQ(1, delegates.front().rank);
}
TEST(StableAbiDelegateProviderTest, CreateDelegateWithStableXNNPack) {
auto delegates = CreateDelegates(std::string(kTestSettingsSrcDir) +
kGoodXNNPackDelegateSettings);
EXPECT_EQ(1, delegates.size());
EXPECT_EQ("STABLE_DELEGATE", delegates.front().provider->GetName());
EXPECT_NE(nullptr, delegates.front().delegate.get());
EXPECT_EQ(1, delegates.front().rank);
pthreadpool_t threadpool = static_cast<pthreadpool_t>(
TfLiteXNNPackDelegateGetThreadPool(delegates.front().delegate.get()));
EXPECT_EQ(5, pthreadpool_get_threads_count(threadpool));
}
TEST(StableAbiDelegateProviderTest, CreateDelegateFailedWithInvalidSettings) {
std::vector<std::string> invalid_settings_names = {
kBadMissingFile, kBadInvalidSettings, kBadMissingStableDelegateSettings,
kBadMissingDelegatePathSettings};
for (const std::string& name : invalid_settings_names) {
auto delegates = CreateDelegates(std::string(kTestSettingsSrcDir) + name);
EXPECT_EQ(0, delegates.size());
}
}
TEST(StableAbiDelegateProviderTest, CreateDelegateFailedWithBlankSettingsPath) {
auto delegates = CreateDelegates("");
EXPECT_EQ(0, delegates.size());
}
} // namespace
} // namespace tools
} // namespace tflite
@@ -0,0 +1,4 @@
// Test only invalid delegate settings file
{
"invalid": "invalid"
}
@@ -0,0 +1,6 @@
// Test only TF Lite settings file. Missing a delegate path in the stable
// delegate settings.
{
"delegate": "NONE",
"stable_delegate_loader_settings": {}
}
@@ -0,0 +1,4 @@
// Test only TF Lite settings file without stable delegate loader settings.
{
"delegate": "NONE"
}
@@ -0,0 +1,11 @@
// Test only sample stable delegate settings file.
//
// This file follows the TFLiteSettings message structure in
// tensorflow/lite/acceleration/configuration/configuration.proto
// The stable delegate provider unit tests use it.
{
"delegate": "NONE",
"stable_delegate_loader_settings": {
"delegate_path": "tensorflow/lite/delegates/utils/experimental/sample_stable_delegate/libtensorflowlite_sample_stable_delegate.so"
}
}
@@ -0,0 +1,14 @@
// Test only stable XNNPack delegate settings file.
//
// This file follows the TFLiteSettings message structure in
// tensorflow/lite/acceleration/configuration/configuration.proto
// The stable delegate provider unit tests use it.
{
"delegate": "XNNPACK",
"stable_delegate_loader_settings": {
"delegate_path": "tensorflow/lite/delegates/utils/experimental/stable_delegate/libtensorflowlite_stable_xnnpack_delegate.so"
},
"xnnpack_settings": {
"num_threads": 5
}
}
@@ -0,0 +1,131 @@
/* 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 <sstream>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/external/external_delegate.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
namespace tflite {
namespace tools {
// Split a given string to a vector of string using a delimiter character
std::vector<std::string> SplitString(const std::string& str, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream ss(str);
while (std::getline(ss, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
// External delegate provider used to dynamically load delegate libraries
// Note: Assumes the lifetime of the provider exceeds the usage scope of
// the generated delegates.
class ExternalDelegateProvider : public DelegateProvider {
public:
ExternalDelegateProvider() {
default_params_.AddParam("external_delegate_path",
ToolParam::Create<std::string>(""));
default_params_.AddParam("external_delegate_options",
ToolParam::Create<std::string>(""));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "EXTERNAL"; }
};
REGISTER_DELEGATE_PROVIDER(ExternalDelegateProvider);
std::vector<Flag> ExternalDelegateProvider::CreateFlags(
ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<std::string>("external_delegate_path", params,
"The library path for the underlying external."),
CreateFlag<std::string>(
"external_delegate_options", params,
"A list of comma-separated options to be passed to the external "
"delegate. Each option is a colon-separated key-value pair, e.g. "
"option_name:option_value.")};
return flags;
}
void ExternalDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, std::string, "external_delegate_path",
"External delegate path", verbose);
LOG_TOOL_PARAM(params, std::string, "external_delegate_options",
"External delegate options", verbose);
}
TfLiteDelegatePtr ExternalDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
std::string lib_path = params.Get<std::string>("external_delegate_path");
if (!lib_path.empty()) {
auto delegate_options =
TfLiteExternalDelegateOptionsDefault(lib_path.c_str());
// Parse delegate options
const std::vector<std::string> options =
SplitString(params.Get<std::string>("external_delegate_options"), ';');
std::vector<std::string> keys, values;
// We reserve the memory here to avoid memory pointer change during
// insertion to vectors above.
keys.reserve(options.size());
values.reserve(options.size());
for (const auto& option : options) {
auto key_value = SplitString(option, ':');
if (key_value.size() == 2) {
// The inserted (key,value) pair has to outlive the
// TfLiteExternalDelegateCreate call, therefore, we use two vectors
// 'keys' and 'values' to achieve this.
// Also, we will insert the memory pointer of key and value to
// delegate_options later, we have to ensure the pointer won't change by
// reserving the memory earlier.
keys.emplace_back(key_value[0]);
values.emplace_back(key_value[1]);
delegate_options.insert(&delegate_options, keys.back().c_str(),
values.back().c_str());
}
}
auto external_delegate = TfLiteExternalDelegateCreate(&delegate_options);
return TfLiteDelegatePtr(external_delegate, [](TfLiteDelegate* delegate) {
TfLiteExternalDelegateDelete(delegate);
});
}
return CreateNullDelegate();
}
std::pair<TfLiteDelegatePtr, int>
ExternalDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr), params.GetPosition<std::string>(
"external_delegate_path"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,219 @@
/* 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 <utility>
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#if TFLITE_SUPPORTS_GPU_DELEGATE
#include "tensorflow/lite/delegates/gpu/delegate.h"
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) || \
(TARGET_OS_OSX && TARGET_CPU_ARM64)
// Only enable metal delegate when using a real iPhone device or Apple Silicon.
#define REAL_IPHONE_DEVICE
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#endif
#endif
namespace tflite {
namespace tools {
class GpuDelegateProvider : public DelegateProvider {
public:
GpuDelegateProvider() {
default_params_.AddParam("use_gpu", ToolParam::Create<bool>(false));
#if TFLITE_SUPPORTS_GPU_DELEGATE || defined(REAL_IPHONE_DEVICE)
default_params_.AddParam("gpu_precision_loss_allowed",
ToolParam::Create<bool>(true));
default_params_.AddParam("gpu_experimental_enable_quant",
ToolParam::Create<bool>(true));
#endif
#if TFLITE_SUPPORTS_GPU_DELEGATE
default_params_.AddParam("gpu_inference_for_sustained_speed",
ToolParam::Create<bool>(false));
default_params_.AddParam("gpu_backend", ToolParam::Create<std::string>(""));
#endif
#if defined(REAL_IPHONE_DEVICE)
default_params_.AddParam("gpu_wait_type",
ToolParam::Create<std::string>(""));
#endif
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "GPU"; }
};
REGISTER_DELEGATE_PROVIDER(GpuDelegateProvider);
std::vector<Flag> GpuDelegateProvider::CreateFlags(ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<bool>("use_gpu", params, "use gpu"),
#if TFLITE_SUPPORTS_GPU_DELEGATE || defined(REAL_IPHONE_DEVICE)
CreateFlag<bool>("gpu_precision_loss_allowed", params,
"Allow to process computation in lower precision than "
"FP32 in GPU. By default, it's enabled."),
CreateFlag<bool>("gpu_experimental_enable_quant", params,
"Whether to enable the GPU delegate to run quantized "
"models or not. By default, it's enabled."),
#endif
#if TFLITE_SUPPORTS_GPU_DELEGATE
CreateFlag<bool>("gpu_inference_for_sustained_speed", params,
"Whether to prefer maximizing the throughput. This mode "
"will help when the same delegate will be used repeatedly "
"on multiple inputs. This is supported on non-iOS "
"platforms. By default, it's disabled."),
CreateFlag<std::string>(
"gpu_backend", params,
"Force the GPU delegate to use a particular backend for execution, and "
"fail if unsuccessful. Should be one of: cl, gl"),
#endif
#if defined(REAL_IPHONE_DEVICE)
CreateFlag<std::string>(
"gpu_wait_type", params,
"GPU wait type. Should be one of the following: passive, active, "
"do_not_wait, aggressive"),
#endif
};
return flags;
}
void GpuDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, bool, "use_gpu", "Use gpu", verbose);
#if TFLITE_SUPPORTS_GPU_DELEGATE || defined(REAL_IPHONE_DEVICE)
LOG_TOOL_PARAM(params, bool, "gpu_precision_loss_allowed",
"Allow lower precision in gpu", verbose);
LOG_TOOL_PARAM(params, bool, "gpu_experimental_enable_quant",
"Enable running quant models in gpu", verbose);
#endif
#if TFLITE_SUPPORTS_GPU_DELEGATE
LOG_TOOL_PARAM(params, bool, "gpu_inference_for_sustained_speed",
"Prefer maximizing the throughput in gpu", verbose);
LOG_TOOL_PARAM(params, std::string, "gpu_backend", "GPU backend", verbose);
#endif
#if defined(REAL_IPHONE_DEVICE)
LOG_TOOL_PARAM(params, std::string, "gpu_wait_type", "GPU delegate wait type",
verbose);
#endif
}
TfLiteDelegatePtr GpuDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
TfLiteDelegatePtr delegate = CreateNullDelegate();
if (params.Get<bool>("use_gpu")) {
#if TFLITE_SUPPORTS_GPU_DELEGATE
TfLiteGpuDelegateOptionsV2 gpu_opts = TfLiteGpuDelegateOptionsV2Default();
if (params.Get<bool>("gpu_precision_loss_allowed")) {
gpu_opts.inference_priority1 = TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY;
gpu_opts.inference_priority2 =
TFLITE_GPU_INFERENCE_PRIORITY_MIN_MEMORY_USAGE;
gpu_opts.inference_priority3 =
TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION;
}
if (params.Get<bool>("gpu_experimental_enable_quant")) {
gpu_opts.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_ENABLE_QUANT;
}
if (params.Get<bool>("gpu_inference_for_sustained_speed")) {
gpu_opts.inference_preference =
TFLITE_GPU_INFERENCE_PREFERENCE_SUSTAINED_SPEED;
}
std::string gpu_backend = params.Get<std::string>("gpu_backend");
if (!gpu_backend.empty()) {
if (gpu_backend == "cl") {
gpu_opts.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_CL_ONLY;
} else if (gpu_backend == "gl") {
gpu_opts.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_GL_ONLY;
}
}
gpu_opts.max_delegated_partitions =
params.Get<int>("max_delegated_partitions");
#ifdef TFLITE_DEBUG_DELEGATE
gpu_opts.first_delegate_node_index =
params.Get<int>("first_delegate_node_index");
gpu_opts.last_delegate_node_index =
params.Get<int>("last_delegate_node_index");
#endif // TFLITE_DEBUG_DELEGATE
#ifdef TFLITE_GPU_ENABLE_INVOKE_LOOP
gpu_opts.gpu_invoke_loop_times = params.Get<int>("gpu_invoke_loop_times");
#endif // TFLITE_GPU_ENABLE_INVOKE_LOOP
// Serialization.
std::string serialize_dir =
params.Get<std::string>("delegate_serialize_dir");
std::string serialize_token =
params.Get<std::string>("delegate_serialize_token");
if (!serialize_dir.empty() && !serialize_token.empty()) {
gpu_opts.experimental_flags =
gpu_opts.experimental_flags |
TFLITE_GPU_EXPERIMENTAL_FLAGS_ENABLE_SERIALIZATION;
gpu_opts.serialization_dir = serialize_dir.c_str();
gpu_opts.model_token = serialize_token.c_str();
}
delegate = evaluation::CreateGPUDelegate(&gpu_opts);
#elif defined(REAL_IPHONE_DEVICE)
TFLGpuDelegateOptions gpu_opts = {0};
gpu_opts.allow_precision_loss =
params.Get<bool>("gpu_precision_loss_allowed");
gpu_opts.enable_quantization =
params.Get<bool>("gpu_experimental_enable_quant");
std::string string_gpu_wait_type = params.Get<std::string>("gpu_wait_type");
if (!string_gpu_wait_type.empty()) {
TFLGpuDelegateWaitType wait_type = TFLGpuDelegateWaitTypePassive;
if (string_gpu_wait_type == "passive") {
wait_type = TFLGpuDelegateWaitTypePassive;
} else if (string_gpu_wait_type == "active") {
wait_type = TFLGpuDelegateWaitTypeActive;
} else if (string_gpu_wait_type == "do_not_wait") {
wait_type = TFLGpuDelegateWaitTypeDoNotWait;
} else if (string_gpu_wait_type == "aggressive") {
wait_type = TFLGpuDelegateWaitTypeAggressive;
}
gpu_opts.wait_type = wait_type;
}
delegate = TfLiteDelegatePtr(TFLGpuDelegateCreate(&gpu_opts),
&TFLGpuDelegateDelete);
#else
TFLITE_LOG(WARN) << "The GPU delegate compile options are only supported "
"on Android or iOS platforms or when the tool was "
"built with -DCL_DELEGATE_NO_GL.";
delegate = evaluation::CreateGPUDelegate();
#endif
if (!delegate.get()) {
TFLITE_LOG(WARN) << "GPU acceleration is unsupported on this platform.";
}
}
return delegate;
}
std::pair<TfLiteDelegatePtr, int>
GpuDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr), params.GetPosition<bool>("use_gpu"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,118 @@
/* 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 <utility>
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#if defined(TFLITE_ENABLE_HEXAGON)
#include "tensorflow/lite/delegates/hexagon/hexagon_delegate.h"
#endif
namespace tflite {
namespace tools {
class HexagonDelegateProvider : public DelegateProvider {
public:
HexagonDelegateProvider() {
#if defined(TFLITE_ENABLE_HEXAGON)
default_params_.AddParam("use_hexagon", ToolParam::Create<bool>(false));
default_params_.AddParam("hexagon_lib_path",
ToolParam::Create<std::string>("/data/local/tmp"));
default_params_.AddParam("hexagon_profiling",
ToolParam::Create<bool>(false));
#endif
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "Hexagon"; }
};
REGISTER_DELEGATE_PROVIDER(HexagonDelegateProvider);
std::vector<Flag> HexagonDelegateProvider::CreateFlags(
ToolParams* params) const {
#if defined(TFLITE_ENABLE_HEXAGON)
std::vector<Flag> flags = {
CreateFlag<bool>("use_hexagon", params, "Use Hexagon delegate"),
CreateFlag<std::string>(
"hexagon_lib_path", params,
"The library path for the underlying Hexagon libraries. The library "
"path ONLY for the libhexagon_nn_skel*.so files. For "
"libhexagon_interface.so, it needs to be on a system library search "
"path such as LD_LIBRARY_PATH."),
CreateFlag<bool>("hexagon_profiling", params,
"Enables Hexagon profiling")};
return flags;
#else
return {};
#endif
}
void HexagonDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
#if defined(TFLITE_ENABLE_HEXAGON)
LOG_TOOL_PARAM(params, bool, "use_hexagon", "Use Hexagon", verbose);
LOG_TOOL_PARAM(params, std::string, "hexagon_lib_path", "Hexagon lib path",
verbose);
LOG_TOOL_PARAM(params, bool, "hexagon_profiling", "Hexagon profiling",
verbose);
#endif
}
TfLiteDelegatePtr HexagonDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
TfLiteDelegatePtr delegate = CreateNullDelegate();
#if defined(TFLITE_ENABLE_HEXAGON)
if (params.Get<bool>("use_hexagon")) {
TfLiteHexagonDelegateOptions options = {0};
options.print_graph_profile = params.Get<bool>("hexagon_profiling");
options.max_delegated_partitions =
params.Get<int>("max_delegated_partitions");
options.min_nodes_per_partition =
params.Get<int>("min_nodes_per_partition");
delegate = evaluation::CreateHexagonDelegate(
&options, params.Get<std::string>("hexagon_lib_path"));
if (!delegate.get()) {
TFLITE_LOG(WARN)
<< "Could not create Hexagon delegate: platform may not support "
"delegate or required libraries are missing";
}
}
#endif
return delegate;
}
std::pair<TfLiteDelegatePtr, int>
HexagonDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
int rank = 0;
#if defined(TFLITE_ENABLE_HEXAGON)
rank = params.GetPosition<bool>("use_hexagon");
#endif
return std::make_pair(std::move(ptr), rank);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,116 @@
/* Copyright 2026 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 <utility>
#include <vector>
#include "tensorflow/lite/tools/command_line_flags.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/tool_params.h"
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
#include "tensorflow/lite/delegates/hexagon_ynn/hexagon_ynn_delegate.h"
#endif
namespace tflite {
namespace tools {
class HexagonYnnDelegateProvider : public DelegateProvider {
public:
HexagonYnnDelegateProvider() {
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
default_params_.AddParam("use_hexagon_ynn", ToolParam::Create<bool>(false));
default_params_.AddParam("hexagon_ynn_lib_path",
ToolParam::Create<std::string>("/data/local/tmp"));
#endif
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "HexagonYnn"; }
};
REGISTER_DELEGATE_PROVIDER(HexagonYnnDelegateProvider);
std::vector<Flag> HexagonYnnDelegateProvider::CreateFlags(
ToolParams* params) const {
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
std::vector<Flag> flags = {
CreateFlag<bool>("use_hexagon_ynn", params,
"Use the YNNPACK Hexagon DSP delegate"),
CreateFlag<std::string>(
"hexagon_ynn_lib_path", params,
"Path to the directory containing libdsp_graph.so (ARM stub) "
"and the dsp/ subdirectory with libdsp_graph_skel.so + "
"libdspynnpack.so.")};
return flags;
#else
return {};
#endif
}
void HexagonYnnDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
LOG_TOOL_PARAM(params, bool, "use_hexagon_ynn", "Use HexagonYnn", verbose);
LOG_TOOL_PARAM(params, std::string, "hexagon_ynn_lib_path",
"HexagonYnn lib path", verbose);
#endif
}
TfLiteDelegatePtr HexagonYnnDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
TfLiteDelegatePtr delegate = CreateNullDelegate();
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
if (params.Get<bool>("use_hexagon_ynn")) {
TfLiteHexagonYnnDelegateOptions options =
TfLiteHexagonYnnDelegateOptionsDefault();
options.max_delegated_partitions =
params.Get<int>("max_delegated_partitions");
options.min_nodes_per_partition =
params.Get<int>("min_nodes_per_partition");
TfLiteHexagonYnnInit(
params.Get<std::string>("hexagon_ynn_lib_path").c_str());
delegate = TfLiteDelegatePtr(TfLiteHexagonYnnDelegateCreate(&options),
TfLiteHexagonYnnDelegateDelete);
if (!delegate.get()) {
TFLITE_LOG(WARN)
<< "Could not create HexagonYnn delegate: platform may not "
"support delegate or required libraries are missing";
}
}
#endif
return delegate;
}
std::pair<TfLiteDelegatePtr, int>
HexagonYnnDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
int rank = 0;
#if defined(TFLITE_ENABLE_HEXAGON_YNN)
rank = params.GetPosition<bool>("use_hexagon_ynn");
#endif
return std::make_pair(std::move(ptr), rank);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,293 @@
/* 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 <utility>
#include <vector>
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/nnapi/nnapi_implementation.h"
#include "tensorflow/lite/nnapi/nnapi_util.h"
#include "tensorflow/lite/nnapi/sl/include/SupportLibrary.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
namespace tflite {
namespace tools {
namespace {
using nnapi::NnApiSupportLibrary;
// StatefulNnApiDelegate that holds onto an NnApiSupportLibrary instance
// passed to the constructor for later destruction.
// Note that the support library must outlive the delegate.
class NnApiSupportLibraryDelegate : public StatefulNnApiDelegate {
public:
NnApiSupportLibraryDelegate(const NnApiSupportLibrary* nnapi_sl,
Options options)
: StatefulNnApiDelegate(nnapi_sl->getFL5(), options),
nnapi_sl_(nnapi_sl) {}
const NnApiSupportLibrary* get_nnapi_sl() const { return nnapi_sl_; }
private:
const NnApiSupportLibrary* const nnapi_sl_;
};
} // namespace
class NnapiDelegateProvider : public DelegateProvider {
public:
NnapiDelegateProvider() {
default_params_.AddParam("use_nnapi", ToolParam::Create<bool>(false));
default_params_.AddParam("nnapi_execution_preference",
ToolParam::Create<std::string>(""));
default_params_.AddParam("nnapi_execution_priority",
ToolParam::Create<std::string>(""));
default_params_.AddParam("nnapi_accelerator_name",
ToolParam::Create<std::string>(""));
default_params_.AddParam("disable_nnapi_cpu",
ToolParam::Create<bool>(true));
default_params_.AddParam("nnapi_allow_fp16",
ToolParam::Create<bool>(false));
default_params_.AddParam("nnapi_allow_dynamic_dimensions",
ToolParam::Create<bool>(false));
default_params_.AddParam("nnapi_use_burst_mode",
ToolParam::Create<bool>(false));
default_params_.AddParam("nnapi_support_library_path",
ToolParam::Create<std::string>(""));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "NNAPI"; }
};
REGISTER_DELEGATE_PROVIDER(NnapiDelegateProvider);
std::vector<Flag> NnapiDelegateProvider::CreateFlags(ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<bool>("use_nnapi", params, "use nnapi delegate api"),
CreateFlag<std::string>("nnapi_execution_preference", params,
"execution preference for nnapi delegate. Should "
"be one of the following: fast_single_answer, "
"sustained_speed, low_power, undefined"),
CreateFlag<std::string>("nnapi_execution_priority", params,
"The model execution priority in nnapi, and it "
"should be one of the following: default, low, "
"medium and high. This requires Android 11+."),
CreateFlag<std::string>(
"nnapi_accelerator_name", params,
"the name of the nnapi accelerator to use (requires Android Q+)"),
CreateFlag<bool>("disable_nnapi_cpu", params,
"Disable the NNAPI CPU device"),
CreateFlag<bool>("nnapi_allow_fp16", params,
"Allow fp32 computation to be run in fp16"),
CreateFlag<bool>(
"nnapi_allow_dynamic_dimensions", params,
"Whether to allow dynamic dimension sizes without re-compilation. "
"This requires Android 9+."),
CreateFlag<bool>(
"nnapi_use_burst_mode", params,
"use NNAPI Burst mode if supported. Burst mode allows accelerators "
"to efficiently manage resources, which would significantly reduce "
"overhead especially if the same delegate instance is to be used for "
"multiple inferences."),
CreateFlag<std::string>(
"nnapi_support_library_path", params,
"Path from which NNAPI support library will be loaded to construct "
"the delegate. In order to use NNAPI delegate with support library, "
"--nnapi_accelerator_name must be specified and must be equal to one "
"of the devices provided by the support library."),
};
return flags;
}
void NnapiDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, bool, "use_nnapi", "Use NNAPI", verbose);
if (!params.Get<bool>("use_nnapi")) return;
LOG_TOOL_PARAM(params, std::string, "nnapi_execution_preference",
"NNAPI execution preference", verbose);
LOG_TOOL_PARAM(params, std::string, "nnapi_execution_priority",
"Model execution priority in nnapi", verbose);
LOG_TOOL_PARAM(params, std::string, "nnapi_accelerator_name",
"NNAPI accelerator name", verbose);
std::string string_device_names_list =
nnapi::GetStringDeviceNamesList(NnApiImplementation());
// Print available devices when possible as it's informative.
if (!string_device_names_list.empty()) {
TFLITE_LOG(INFO) << "NNAPI accelerators available: ["
<< string_device_names_list << "]";
}
LOG_TOOL_PARAM(params, bool, "disable_nnapi_cpu", "Disable NNAPI cpu",
verbose);
LOG_TOOL_PARAM(params, bool, "nnapi_allow_fp16", "Allow fp16 in NNAPI",
verbose);
LOG_TOOL_PARAM(params, bool, "nnapi_allow_dynamic_dimensions",
"Allow dynamic dimensions in NNAPI", verbose);
LOG_TOOL_PARAM(params, bool, "nnapi_use_burst_mode",
"Use burst mode in NNAPI", verbose);
}
TfLiteDelegatePtr NnapiDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
TfLiteDelegatePtr null_delegate = CreateNullDelegate();
if (params.Get<bool>("use_nnapi")) {
StatefulNnApiDelegate::Options options;
std::string accelerator_name =
params.Get<std::string>("nnapi_accelerator_name");
if (!accelerator_name.empty()) {
options.accelerator_name = accelerator_name.c_str();
} else {
options.disallow_nnapi_cpu = params.Get<bool>("disable_nnapi_cpu");
}
if (params.Get<bool>("nnapi_allow_fp16")) {
options.allow_fp16 = true;
}
if (params.Get<bool>("nnapi_allow_dynamic_dimensions")) {
options.allow_dynamic_dimensions = true;
}
if (params.Get<bool>("nnapi_use_burst_mode")) {
options.use_burst_computation = true;
}
std::string string_execution_preference =
params.Get<std::string>("nnapi_execution_preference");
// Only set execution preference if user explicitly passes one. Otherwise,
// leave it as whatever NNAPI has as the default.
if (!string_execution_preference.empty()) {
tflite::StatefulNnApiDelegate::Options::ExecutionPreference
execution_preference =
tflite::StatefulNnApiDelegate::Options::kUndefined;
if (string_execution_preference == "low_power") {
execution_preference =
tflite::StatefulNnApiDelegate::Options::kLowPower;
} else if (string_execution_preference == "sustained_speed") {
execution_preference =
tflite::StatefulNnApiDelegate::Options::kSustainedSpeed;
} else if (string_execution_preference == "fast_single_answer") {
execution_preference =
tflite::StatefulNnApiDelegate::Options::kFastSingleAnswer;
} else if (string_execution_preference == "undefined") {
execution_preference =
tflite::StatefulNnApiDelegate::Options::kUndefined;
} else {
TFLITE_LOG(WARN) << "The provided value ("
<< string_execution_preference
<< ") is not a valid nnapi execution preference.";
}
options.execution_preference = execution_preference;
}
std::string string_execution_priority =
params.Get<std::string>("nnapi_execution_priority");
// Only set execution priority if user explicitly passes one. Otherwise,
// leave it as whatever NNAPI has as the default.
if (!string_execution_priority.empty()) {
int execution_priority = 0;
if (string_execution_priority == "default") {
execution_priority = ANEURALNETWORKS_PRIORITY_DEFAULT;
} else if (string_execution_priority == "low") {
execution_priority = ANEURALNETWORKS_PRIORITY_LOW;
} else if (string_execution_priority == "medium") {
execution_priority = ANEURALNETWORKS_PRIORITY_MEDIUM;
} else if (string_execution_priority == "high") {
execution_priority = ANEURALNETWORKS_PRIORITY_HIGH;
} else {
TFLITE_LOG(WARN) << "The provided value (" << string_execution_priority
<< ") is not a valid nnapi execution priority.";
}
options.execution_priority = execution_priority;
}
int max_delegated_partitions = params.Get<int>("max_delegated_partitions");
if (max_delegated_partitions >= 0) {
options.max_number_delegated_partitions = max_delegated_partitions;
}
// Serialization.
std::string serialize_dir =
params.Get<std::string>("delegate_serialize_dir");
std::string serialize_token =
params.Get<std::string>("delegate_serialize_token");
if (!serialize_dir.empty() && !serialize_token.empty()) {
options.cache_dir = serialize_dir.c_str();
options.model_token = serialize_token.c_str();
}
if (params.Get<std::string>("nnapi_support_library_path").empty()) {
const auto* nnapi_impl = NnApiImplementation();
if (!nnapi_impl->nnapi_exists) {
TFLITE_LOG(WARN)
<< "NNAPI acceleration is unsupported on this platform.";
return null_delegate;
}
return TfLiteDelegatePtr(
new StatefulNnApiDelegate(nnapi_impl, options),
[](TfLiteDelegate* delegate) {
delete reinterpret_cast<StatefulNnApiDelegate*>(delegate);
});
} else {
std::string sl_path =
params.Get<std::string>("nnapi_support_library_path");
auto nnapi_impl = nnapi::loadNnApiSupportLibrary(sl_path);
if (!nnapi_impl) {
TFLITE_LOG(WARN) << "Couldn't load NNAPI support library from path: "
<< sl_path;
return null_delegate;
}
return TfLiteDelegatePtr(
new NnApiSupportLibraryDelegate(nnapi_impl.release(), options),
[](TfLiteDelegate* delegate) {
NnApiSupportLibraryDelegate* sl_delegate =
reinterpret_cast<NnApiSupportLibraryDelegate*>(delegate);
const NnApiSupportLibrary* sl = sl_delegate->get_nnapi_sl();
delete sl_delegate;
delete sl;
});
}
} else if (!params.Get<std::string>("nnapi_accelerator_name").empty()) {
TFLITE_LOG(WARN)
<< "`--use_nnapi=true` must be set for the provided NNAPI accelerator ("
<< params.Get<std::string>("nnapi_accelerator_name") << ") to be used.";
} else if (!params.Get<std::string>("nnapi_execution_preference").empty()) {
TFLITE_LOG(WARN) << "`--use_nnapi=true` must be set for the provided NNAPI "
"execution preference ("
<< params.Get<std::string>("nnapi_execution_preference")
<< ") to be used.";
}
return null_delegate;
}
std::pair<TfLiteDelegatePtr, int>
NnapiDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr), params.GetPosition<bool>("use_nnapi"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,115 @@
/* 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 <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/evaluation/utils.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
class XnnpackDelegateProvider : public DelegateProvider {
public:
XnnpackDelegateProvider() {
default_params_.AddParam("use_xnnpack", ToolParam::Create<bool>(false));
default_params_.AddParam("xnnpack_force_fp16",
ToolParam::Create<bool>(false));
default_params_.AddParam("xnnpack_weight_cache_file_path",
ToolParam::Create<std::string>(""));
default_params_.AddParam("xnnpack_runtime_flags",
ToolParam::Create<int>(0));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "XNNPACK"; }
};
REGISTER_DELEGATE_PROVIDER(XnnpackDelegateProvider);
std::vector<Flag> XnnpackDelegateProvider::CreateFlags(
ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<bool>("use_xnnpack", params,
"explicitly apply the XNNPACK delegate. Note the "
"XNNPACK delegate could "
"be implicitly applied by the TF Lite runtime "
"regardless the value of "
"this parameter. To disable this implicit application, "
"set the value to "
"false explicitly."),
CreateFlag<bool>("xnnpack_force_fp16", params,
"enforce float16 inference."),
CreateFlag<std::string>("xnnpack_weight_cache_file_path", params,
"enable file-backed weight caching."),
CreateFlag<int>("xnnpack_runtime_flags", params,
"Extra flags to pass to XNNPACK runtime."),
};
return flags;
}
void XnnpackDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, bool, "use_xnnpack", "Use xnnpack", verbose);
LOG_TOOL_PARAM(params, bool, "xnnpack_force_fp16", "xnnpack_force_fp16",
verbose);
LOG_TOOL_PARAM(params, std::string, "xnnpack_weight_cache_file_path",
"xnnpack_weight_cache_file_path", verbose);
LOG_TOOL_PARAM(params, int, "xnnpack_runtime_flags",
"Extra flags for XNNPACK runtime", verbose);
}
TfLiteDelegatePtr XnnpackDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
if (params.Get<bool>("use_xnnpack")) {
auto opts = evaluation::XNNPackDelegateOptionsDefault();
opts.num_threads = params.Get<int32_t>("num_threads");
// Note that we don't want to use the thread pool for num_threads == 1.
if (opts.num_threads <= 1) opts.num_threads = 0;
if (params.Get<bool>("xnnpack_force_fp16")) {
opts.flags |= TFLITE_XNNPACK_DELEGATE_FLAG_FORCE_FP16;
}
opts.runtime_flags = params.Get<int>("xnnpack_runtime_flags");
const std::string path =
params.Get<std::string>("xnnpack_weight_cache_file_path");
if (!path.empty()) {
opts.weight_cache_file_path = path.c_str();
}
return evaluation::CreateXNNPACKDelegate(&opts);
}
return CreateNullDelegate();
}
std::pair<TfLiteDelegatePtr, int>
XnnpackDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr),
params.GetPosition<bool>("use_xnnpack"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,82 @@
/* Copyright 2024 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 <cstdint>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
namespace {
TEST(XNNPackDelegateProviderTest, Test) {
const std::string kFakeCacheParam =
testing::TempDir() + "/XNNPackDelegateProviderTest.xnnpack_cache";
const auto& providers = GetRegisteredDelegateProviders();
ASSERT_EQ(providers.size(), 1);
ToolParams params;
const auto& xnnpack_provider = providers[0];
ASSERT_NE(xnnpack_provider, nullptr);
params.Merge(xnnpack_provider->DefaultParams());
params.AddParam("num_threads", ToolParam::Create<int32_t>(-1));
EXPECT_TRUE(params.HasParam("use_xnnpack"));
EXPECT_FALSE(params.HasValueSet<bool>("use_xnnpack"));
ASSERT_NE(params.GetParam("use_xnnpack"), nullptr);
EXPECT_TRUE(params.HasParam("xnnpack_force_fp16"));
EXPECT_FALSE(params.HasValueSet<bool>("xnnpack_force_fp16"));
ASSERT_NE(params.GetParam("xnnpack_force_fp16"), nullptr);
EXPECT_TRUE(params.HasParam("xnnpack_weight_cache_file_path"));
EXPECT_FALSE(
params.HasValueSet<std::string>("xnnpack_weight_cache_file_path"));
ASSERT_NE(params.GetParam("xnnpack_weight_cache_file_path"), nullptr);
params.Set<bool>("use_xnnpack", true, /*position=*/0);
{
TfLiteDelegatePtr delegate = xnnpack_provider->CreateTfLiteDelegate(params);
const TfLiteXNNPackDelegateOptions* options =
TfLiteXNNPackDelegateGetOptions(delegate.get());
ASSERT_NE(options, nullptr);
EXPECT_EQ(options->weight_cache_file_path, nullptr);
}
params.Set<bool>("xnnpack_force_fp16", true, /*position=*/1);
params.Set<std::string>("xnnpack_weight_cache_file_path", kFakeCacheParam,
/*position=*/2);
{
TfLiteDelegatePtr delegate = xnnpack_provider->CreateTfLiteDelegate(params);
const TfLiteXNNPackDelegateOptions* options =
TfLiteXNNPackDelegateGetOptions(delegate.get());
ASSERT_NE(options, nullptr);
EXPECT_THAT(options->weight_cache_file_path,
testing::StrEq(kFakeCacheParam));
EXPECT_TRUE(options->flags & TFLITE_XNNPACK_DELEGATE_FLAG_FORCE_FP16);
}
}
} // namespace
} // namespace tools
} // namespace tflite
@@ -0,0 +1,120 @@
/* Copyright 2026 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 <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/delegates/ynnpack/ynnpack_delegate.h"
#include "tensorflow/lite/tools/command_line_flags.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "tensorflow/lite/tools/tool_params.h"
namespace tflite {
namespace tools {
class YnnpackDelegateProvider : public DelegateProvider {
public:
YnnpackDelegateProvider() {
default_params_.AddParam("use_ynnpack", ToolParam::Create<bool>(false));
default_params_.AddParam("ynnpack_static_shape",
ToolParam::Create<bool>(false));
default_params_.AddParam("ynnpack_fast_math",
ToolParam::Create<bool>(false));
default_params_.AddParam("ynnpack_consistent_arithmetic",
ToolParam::Create<bool>(false));
default_params_.AddParam("ynnpack_no_excess_precision",
ToolParam::Create<bool>(false));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int> CreateRankedTfLiteDelegate(
const ToolParams& params) const final;
std::string GetName() const final { return "YNNPACK"; }
};
REGISTER_DELEGATE_PROVIDER(YnnpackDelegateProvider);
std::vector<Flag> YnnpackDelegateProvider::CreateFlags(
ToolParams* params) const {
std::vector<Flag> flags = {
CreateFlag<bool>("use_ynnpack", params,
"explicitly apply the YNNPACK delegate."),
CreateFlag<bool>(
"ynnpack_static_shape", params,
"make input shapes static instead of dynamic. May improve invoke "
"performance at the cost of much more expensive reshape."),
CreateFlag<bool>("ynnpack_fast_math", params,
"enable YNN_FLAG_FAST_MATH."),
CreateFlag<bool>("ynnpack_consistent_arithmetic", params,
"enable YNN_FLAG_CONSISTENT_ARITHMETIC."),
CreateFlag<bool>("ynnpack_no_excess_precision", params,
"enable YNN_FLAG_NO_EXCESS_PRECISION."),
};
return flags;
}
void YnnpackDelegateProvider::LogParams(const ToolParams& params,
bool verbose) const {
LOG_TOOL_PARAM(params, bool, "use_ynnpack", "Use ynnpack", verbose);
LOG_TOOL_PARAM(params, bool, "ynnpack_static_shape", "YNNPACK static shape",
verbose);
LOG_TOOL_PARAM(params, bool, "ynnpack_fast_math", "YNNPACK fast math",
verbose);
LOG_TOOL_PARAM(params, bool, "ynnpack_consistent_arithmetic",
"YNNPACK consistent arithmetic", verbose);
LOG_TOOL_PARAM(params, bool, "ynnpack_no_excess_precision",
"YNNPACK no excess precision", verbose);
}
TfLiteDelegatePtr YnnpackDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
if (params.Get<bool>("use_ynnpack")) {
auto opts = TfLiteYNNPackDelegateOptionsDefault();
opts.num_threads = params.Get<int32_t>("num_threads");
// Note that we don't want to use the thread pool for num_threads == 1.
if (opts.num_threads <= 1) opts.num_threads = 0;
opts.static_shape = params.Get<bool>("ynnpack_static_shape");
opts.fast_math = params.Get<bool>("ynnpack_fast_math");
opts.consistent_arithmetic =
params.Get<bool>("ynnpack_consistent_arithmetic");
opts.no_excess_precision = params.Get<bool>("ynnpack_no_excess_precision");
return TfLiteDelegatePtr(reinterpret_cast<TfLiteOpaqueDelegate*>(
TfLiteYNNPackDelegateCreate(&opts)),
[](TfLiteOpaqueDelegate* delegate) {
TfLiteYNNPackDelegateDelete(
reinterpret_cast<TfLiteDelegate*>(delegate));
});
}
return CreateNullDelegate();
}
std::pair<TfLiteDelegatePtr, int>
YnnpackDelegateProvider::CreateRankedTfLiteDelegate(
const ToolParams& params) const {
auto ptr = CreateTfLiteDelegate(params);
return std::make_pair(std::move(ptr),
params.GetPosition<bool>("use_ynnpack"));
}
} // namespace tools
} // namespace tflite