| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==============================================================================*/
- #include "tensorflow/lite/micro/micro_interpreter.h"
- #include <cstdarg>
- #include <cstddef>
- #include <cstdint>
- #include "flatbuffers/flatbuffers.h" // from @flatbuffers
- #include "tensorflow/lite/c/common.h"
- #include "tensorflow/lite/core/api/error_reporter.h"
- #include "tensorflow/lite/core/api/tensor_utils.h"
- #include "tensorflow/lite/micro/memory_helpers.h"
- #include "tensorflow/lite/micro/micro_allocator.h"
- #include "tensorflow/lite/micro/micro_error_reporter.h"
- #include "tensorflow/lite/micro/micro_op_resolver.h"
- #include "tensorflow/lite/micro/micro_profiler.h"
- #include "tensorflow/lite/schema/schema_generated.h"
- namespace tflite {
- namespace {
- #ifndef TF_LITE_STRIP_ERROR_STRINGS
- const char* OpNameFromRegistration(const TfLiteRegistration* registration) {
- if (registration->builtin_code == BuiltinOperator_CUSTOM) {
- return registration->custom_name;
- } else {
- return EnumNameBuiltinOperator(BuiltinOperator(registration->builtin_code));
- }
- }
- #endif // !defined(TF_LITE_STRIP_ERROR_STRINGS)
- } // namespace
- namespace internal {
- ContextHelper::ContextHelper(ErrorReporter* error_reporter,
- MicroAllocator* allocator, const Model* model)
- : allocator_(allocator), error_reporter_(error_reporter), model_(model) {}
- void* ContextHelper::AllocatePersistentBuffer(TfLiteContext* ctx,
- size_t bytes) {
- return reinterpret_cast<ContextHelper*>(ctx->impl_)
- ->allocator_->AllocatePersistentBuffer(bytes);
- }
- TfLiteStatus ContextHelper::RequestScratchBufferInArena(TfLiteContext* ctx,
- size_t bytes,
- int* buffer_idx) {
- ContextHelper* helper = reinterpret_cast<ContextHelper*>(ctx->impl_);
- return helper->allocator_->RequestScratchBufferInArena(bytes, buffer_idx);
- }
- void* ContextHelper::GetScratchBuffer(TfLiteContext* ctx, int buffer_idx) {
- ContextHelper* helper = reinterpret_cast<ContextHelper*>(ctx->impl_);
- ScratchBufferHandle* handle = helper->scratch_buffer_handles_ + buffer_idx;
- return handle->data;
- }
- void ContextHelper::ReportOpError(struct TfLiteContext* context,
- const char* format, ...) {
- #ifndef TF_LITE_STRIP_ERROR_STRINGS
- ContextHelper* helper = static_cast<ContextHelper*>(context->impl_);
- va_list args;
- va_start(args, format);
- TF_LITE_REPORT_ERROR(helper->error_reporter_, format, args);
- va_end(args);
- #endif
- }
- TfLiteTensor* ContextHelper::GetTensor(const struct TfLiteContext* context,
- int tensor_idx) {
- ContextHelper* helper = static_cast<ContextHelper*>(context->impl_);
- return helper->allocator_->AllocateTempTfLiteTensor(
- helper->model_, helper->eval_tensors_, tensor_idx);
- }
- TfLiteEvalTensor* ContextHelper::GetEvalTensor(
- const struct TfLiteContext* context, int tensor_idx) {
- ContextHelper* helper = reinterpret_cast<ContextHelper*>(context->impl_);
- return &helper->eval_tensors_[tensor_idx];
- }
- void ContextHelper::SetTfLiteEvalTensors(TfLiteEvalTensor* eval_tensors) {
- eval_tensors_ = eval_tensors;
- }
- void ContextHelper::SetScratchBufferHandles(
- ScratchBufferHandle* scratch_buffer_handles) {
- scratch_buffer_handles_ = scratch_buffer_handles;
- }
- } // namespace internal
- MicroInterpreter::MicroInterpreter(const Model* model,
- const MicroOpResolver& op_resolver,
- uint8_t* tensor_arena,
- size_t tensor_arena_size,
- ErrorReporter* error_reporter,
- MicroProfiler* profiler)
- : model_(model),
- op_resolver_(op_resolver),
- error_reporter_(error_reporter),
- allocator_(*MicroAllocator::Create(tensor_arena, tensor_arena_size,
- error_reporter)),
- tensors_allocated_(false),
- initialization_status_(kTfLiteError),
- eval_tensors_(nullptr),
- context_helper_(error_reporter_, &allocator_, model),
- input_tensors_(nullptr),
- output_tensors_(nullptr) {
- Init(profiler);
- }
- MicroInterpreter::MicroInterpreter(const Model* model,
- const MicroOpResolver& op_resolver,
- MicroAllocator* allocator,
- ErrorReporter* error_reporter,
- MicroProfiler* profiler)
- : model_(model),
- op_resolver_(op_resolver),
- error_reporter_(error_reporter),
- allocator_(*allocator),
- tensors_allocated_(false),
- initialization_status_(kTfLiteError),
- eval_tensors_(nullptr),
- context_helper_(error_reporter_, &allocator_, model),
- input_tensors_(nullptr),
- output_tensors_(nullptr) {
- Init(profiler);
- }
- MicroInterpreter::~MicroInterpreter() {
- if (node_and_registrations_ != nullptr) {
- for (size_t i = 0; i < subgraph_->operators()->size(); ++i) {
- TfLiteNode* node = &(node_and_registrations_[i].node);
- const TfLiteRegistration* registration =
- node_and_registrations_[i].registration;
- // registration is allocated outside the interpreter, so double check to
- // make sure it's not nullptr;
- if (registration != nullptr && registration->free != nullptr) {
- registration->free(&context_, node->user_data);
- }
- }
- }
- }
- void MicroInterpreter::Init(MicroProfiler* profiler) {
- const flatbuffers::Vector<flatbuffers::Offset<SubGraph>>* subgraphs =
- model_->subgraphs();
- if (subgraphs->size() != 1) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Only 1 subgraph is currently supported.\n");
- initialization_status_ = kTfLiteError;
- return;
- }
- subgraph_ = (*subgraphs)[0];
- context_.impl_ = static_cast<void*>(&context_helper_);
- context_.ReportError = context_helper_.ReportOpError;
- context_.GetTensor = context_helper_.GetTensor;
- context_.GetEvalTensor = context_helper_.GetEvalTensor;
- context_.recommended_num_threads = 1;
- context_.profiler = profiler;
- initialization_status_ = kTfLiteOk;
- }
- TfLiteStatus MicroInterpreter::AllocateTensors() {
- if (allocator_.StartModelAllocation(model_, op_resolver_,
- &node_and_registrations_,
- &eval_tensors_) != kTfLiteOk) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Failed starting model allocation.\n");
- initialization_status_ = kTfLiteError;
- return kTfLiteError;
- }
- // Update the pointer now that TfLiteEvalTensor allocation has completed on
- // the context helper.
- // TODO(b/16157777): This call would not be needed if ContextHelper rolled
- // into the interpreter.
- context_helper_.SetTfLiteEvalTensors(eval_tensors_);
- context_.tensors_size = subgraph_->tensors()->size();
- // Only allow AllocatePersistentBuffer in Init stage.
- context_.AllocatePersistentBuffer = context_helper_.AllocatePersistentBuffer;
- context_.RequestScratchBufferInArena = nullptr;
- context_.GetScratchBuffer = nullptr;
- for (size_t i = 0; i < subgraph_->operators()->size(); ++i) {
- auto* node = &(node_and_registrations_[i].node);
- auto* registration = node_and_registrations_[i].registration;
- size_t init_data_size;
- const char* init_data;
- if (registration->builtin_code == BuiltinOperator_CUSTOM) {
- init_data = reinterpret_cast<const char*>(node->custom_initial_data);
- init_data_size = node->custom_initial_data_size;
- } else {
- init_data = reinterpret_cast<const char*>(node->builtin_data);
- init_data_size = 0;
- }
- if (registration->init) {
- node->user_data =
- registration->init(&context_, init_data, init_data_size);
- }
- }
- // Both AllocatePersistentBuffer and RequestScratchBufferInArena is
- // available in Prepare stage.
- context_.RequestScratchBufferInArena =
- context_helper_.RequestScratchBufferInArena;
- for (size_t i = 0; i < subgraph_->operators()->size(); ++i) {
- auto* node = &(node_and_registrations_[i].node);
- auto* registration = node_and_registrations_[i].registration;
- if (registration->prepare) {
- TfLiteStatus prepare_status = registration->prepare(&context_, node);
- if (prepare_status != kTfLiteOk) {
- TF_LITE_REPORT_ERROR(
- error_reporter_,
- "Node %s (number %df) failed to prepare with status %d",
- OpNameFromRegistration(registration), i, prepare_status);
- return kTfLiteError;
- }
- }
- allocator_.FinishPrepareNodeAllocations(/*node_id=*/i);
- }
- // Prepare is done, we're ready for Invoke. Memory allocation is no longer
- // allowed. Kernels can only fetch scratch buffers via GetScratchBuffer.
- context_.AllocatePersistentBuffer = nullptr;
- context_.RequestScratchBufferInArena = nullptr;
- context_.GetScratchBuffer = context_helper_.GetScratchBuffer;
- TF_LITE_ENSURE_OK(&context_,
- allocator_.FinishModelAllocation(model_, eval_tensors_,
- &scratch_buffer_handles_));
- // TODO(b/16157777): Remove this when ContextHelper is rolled into this class.
- context_helper_.SetScratchBufferHandles(scratch_buffer_handles_);
- // TODO(b/162311891): Drop these allocations when the interpreter supports
- // handling buffers from TfLiteEvalTensor.
- input_tensors_ =
- reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer(
- sizeof(TfLiteTensor*) * inputs_size()));
- if (input_tensors_ == nullptr) {
- TF_LITE_REPORT_ERROR(
- error_reporter_,
- "Failed to allocate memory for context->input_tensors_, "
- "%d bytes required",
- sizeof(TfLiteTensor*) * inputs_size());
- return kTfLiteError;
- }
- for (size_t i = 0; i < inputs_size(); ++i) {
- input_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor(
- model_, eval_tensors_, inputs().Get(i));
- if (input_tensors_[i] == nullptr) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Failed to initialize input tensor %d", i);
- return kTfLiteError;
- }
- }
- // TODO(b/162311891): Drop these allocations when the interpreter supports
- // handling buffers from TfLiteEvalTensor.
- output_tensors_ =
- reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer(
- sizeof(TfLiteTensor*) * outputs_size()));
- if (output_tensors_ == nullptr) {
- TF_LITE_REPORT_ERROR(
- error_reporter_,
- "Failed to allocate memory for context->output_tensors_, "
- "%d bytes required",
- sizeof(TfLiteTensor*) * outputs_size());
- return kTfLiteError;
- }
- for (size_t i = 0; i < outputs_size(); ++i) {
- output_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor(
- model_, eval_tensors_, outputs().Get(i));
- if (output_tensors_[i] == nullptr) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Failed to initialize output tensor %d", i);
- return kTfLiteError;
- }
- }
- TF_LITE_ENSURE_STATUS(ResetVariableTensors());
- tensors_allocated_ = true;
- return kTfLiteOk;
- }
- TfLiteStatus MicroInterpreter::Invoke() {
- if (initialization_status_ != kTfLiteOk) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Invoke() called after initialization failed\n");
- return kTfLiteError;
- }
- // Ensure tensors are allocated before the interpreter is invoked to avoid
- // difficult to debug segfaults.
- if (!tensors_allocated_) {
- TF_LITE_ENSURE_OK(&context_, AllocateTensors());
- }
- for (size_t i = 0; i < subgraph_->operators()->size(); ++i) {
- auto* node = &(node_and_registrations_[i].node);
- auto* registration = node_and_registrations_[i].registration;
- // This ifdef is needed (even though ScopedMicroProfiler itself is a no-op with
- // -DTF_LITE_STRIP_ERROR_STRINGS) because the function OpNameFromRegistration is
- // only defined for builds with the error strings.
- #if !defined(TF_LITE_STRIP_ERROR_STRINGS)
- ScopedMicroProfiler scoped_profiler(
- OpNameFromRegistration(registration),
- reinterpret_cast<MicroProfiler*>(context_.profiler));
- #endif
- TFLITE_DCHECK(registration->invoke);
- TfLiteStatus invoke_status = registration->invoke(&context_, node);
- // All TfLiteTensor structs used in the kernel are allocated from temp
- // memory in the allocator. This creates a chain of allocations in the
- // temp section. The call below resets the chain of allocations to
- // prepare for the next call.
- allocator_.ResetTempAllocations();
- if (invoke_status == kTfLiteError) {
- TF_LITE_REPORT_ERROR(
- error_reporter_,
- "Node %s (number %d) failed to invoke with status %d",
- OpNameFromRegistration(registration), i, invoke_status);
- return kTfLiteError;
- } else if (invoke_status != kTfLiteOk) {
- return invoke_status;
- }
- }
- return kTfLiteOk;
- }
- TfLiteTensor* MicroInterpreter::input(size_t index) {
- const size_t length = inputs_size();
- if (index >= length) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Input index %d out of range (length is %d)", index,
- length);
- return nullptr;
- }
- return input_tensors_[index];
- }
- TfLiteTensor* MicroInterpreter::output(size_t index) {
- const size_t length = outputs_size();
- if (index >= length) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Output index %d out of range (length is %d)", index,
- length);
- return nullptr;
- }
- return output_tensors_[index];
- }
- TfLiteTensor* MicroInterpreter::tensor(size_t index) {
- const size_t length = tensors_size();
- if (index >= length) {
- TF_LITE_REPORT_ERROR(error_reporter_,
- "Tensor index %d out of range (length is %d)", index,
- length);
- return nullptr;
- }
- return allocator_.AllocatePersistentTfLiteTensor(model_, eval_tensors_,
- index);
- }
- TfLiteStatus MicroInterpreter::ResetVariableTensors() {
- for (size_t i = 0; i < subgraph_->tensors()->size(); ++i) {
- auto* tensor = subgraph_->tensors()->Get(i);
- if (tensor->is_variable()) {
- size_t buffer_size;
- TF_LITE_ENSURE_STATUS(
- TfLiteEvalTensorByteLength(&eval_tensors_[i], &buffer_size));
- int value = 0;
- if (tensor->type() == tflite::TensorType_INT8) {
- value = tensor->quantization()->zero_point()->Get(0);
- }
- memset(eval_tensors_[i].data.raw, value, buffer_size);
- }
- }
- return kTfLiteOk;
- }
- } // namespace tflite
|