| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /* 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/testing/test_utils.h"
- #include "tensorflow/lite/micro/simple_memory_allocator.h"
- namespace tflite {
- namespace testing {
- namespace {
- // TODO(b/141330728): Refactor out of test_utils.cc
- // The variables below (and the AllocatePersistentBuffer function) are only
- // needed for the kernel tests and benchmarks, i.e. where we do not have an
- // interpreter object, and the fully featured MicroAllocator.
- // Currently, these need to be sufficient for all the kernel_tests. If that
- // becomes problematic, we can investigate allowing the arena_size to be
- // specified for each call to PopulatContext.
- constexpr size_t kArenaSize = 10000;
- uint8_t raw_arena_[kArenaSize];
- SimpleMemoryAllocator* simple_memory_allocator_ = nullptr;
- constexpr size_t kBufferAlignment = 16;
- // We store the pointer to the ith scratch buffer to implement the Request/Get
- // ScratchBuffer API for the tests. scratch_buffers_[i] will be the ith scratch
- // buffer and will still be allocated from within raw_arena_.
- constexpr int kNumScratchBuffers = 5;
- uint8_t* scratch_buffers_[kNumScratchBuffers];
- int scratch_buffer_count_ = 0;
- // Note that the context parameter in this function is only needed to match the
- // signature of TfLiteContext::AllocatePersistentBuffer and isn't needed in the
- // implementation because we are assuming a single global
- // simple_memory_allocator_
- void* AllocatePersistentBuffer(TfLiteContext* context, size_t bytes) {
- TFLITE_DCHECK(simple_memory_allocator_ != nullptr);
- return simple_memory_allocator_->AllocateFromTail(bytes, kBufferAlignment);
- }
- TfLiteStatus RequestScratchBufferInArena(TfLiteContext* context, size_t bytes,
- int* buffer_index) {
- TFLITE_DCHECK(simple_memory_allocator_ != nullptr);
- TFLITE_DCHECK(buffer_index != nullptr);
- if (scratch_buffer_count_ == kNumScratchBuffers) {
- TF_LITE_REPORT_ERROR(
- static_cast<ErrorReporter*>(context->impl_),
- "Exceeded the maximum number of scratch tensors allowed (%d).",
- kNumScratchBuffers);
- return kTfLiteError;
- }
- // For tests, we allocate scratch buffers from the tail and keep them around
- // for the lifetime of model. This means that the arena size in the tests will
- // be more than what we would have if the scratch buffers could share memory.
- scratch_buffers_[scratch_buffer_count_] =
- simple_memory_allocator_->AllocateFromTail(bytes, kBufferAlignment);
- TFLITE_DCHECK(scratch_buffers_[scratch_buffer_count_] != nullptr);
- *buffer_index = scratch_buffer_count_++;
- return kTfLiteOk;
- }
- void* GetScratchBuffer(TfLiteContext* context, int buffer_index) {
- TFLITE_DCHECK(scratch_buffer_count_ <= kNumScratchBuffers);
- if (buffer_index >= scratch_buffer_count_) {
- return nullptr;
- }
- return scratch_buffers_[buffer_index];
- }
- TfLiteTensor* GetTensor(const struct TfLiteContext* context, int subgraph_idx) {
- // TODO(b/160894903): Return this value from temp allocated memory.
- return &context->tensors[subgraph_idx];
- }
- } // namespace
- uint8_t F2Q(float value, float min, float max) {
- int32_t result = ZeroPointFromMinMax<uint8_t>(min, max) +
- (value / ScaleFromMinMax<uint8_t>(min, max)) + 0.5f;
- if (result < std::numeric_limits<uint8_t>::min()) {
- result = std::numeric_limits<uint8_t>::min();
- }
- if (result > std::numeric_limits<uint8_t>::max()) {
- result = std::numeric_limits<uint8_t>::max();
- }
- return result;
- }
- // Converts a float value into a signed eight-bit quantized value.
- int8_t F2QS(float value, float min, float max) {
- return F2Q(value, min, max) + std::numeric_limits<int8_t>::min();
- }
- int32_t F2Q32(float value, float scale) {
- double quantized = static_cast<double>(value / scale);
- if (quantized > std::numeric_limits<int32_t>::max()) {
- quantized = std::numeric_limits<int32_t>::max();
- } else if (quantized < std::numeric_limits<int32_t>::min()) {
- quantized = std::numeric_limits<int32_t>::min();
- }
- return static_cast<int>(quantized);
- }
- // TODO(b/141330728): Move this method elsewhere as part clean up.
- void PopulateContext(TfLiteTensor* tensors, int tensors_size,
- ErrorReporter* error_reporter, TfLiteContext* context) {
- simple_memory_allocator_ =
- SimpleMemoryAllocator::Create(error_reporter, raw_arena_, kArenaSize);
- TFLITE_DCHECK(simple_memory_allocator_ != nullptr);
- scratch_buffer_count_ = 0;
- context->tensors_size = tensors_size;
- context->tensors = tensors;
- context->impl_ = static_cast<void*>(error_reporter);
- context->GetExecutionPlan = nullptr;
- context->ResizeTensor = nullptr;
- context->ReportError = ReportOpError;
- context->AddTensors = nullptr;
- context->GetNodeAndRegistration = nullptr;
- context->ReplaceNodeSubsetsWithDelegateKernels = nullptr;
- context->recommended_num_threads = 1;
- context->GetExternalContext = nullptr;
- context->SetExternalContext = nullptr;
- context->GetTensor = GetTensor;
- context->GetEvalTensor = nullptr;
- context->AllocatePersistentBuffer = AllocatePersistentBuffer;
- context->RequestScratchBufferInArena = RequestScratchBufferInArena;
- context->GetScratchBuffer = GetScratchBuffer;
- for (int i = 0; i < tensors_size; ++i) {
- if (context->tensors[i].is_variable) {
- ResetVariableTensor(&context->tensors[i]);
- }
- }
- }
- TfLiteTensor CreateQuantizedTensor(const uint8_t* data, TfLiteIntArray* dims,
- float min, float max, bool is_variable) {
- TfLiteTensor result;
- result.type = kTfLiteUInt8;
- result.data.uint8 = const_cast<uint8_t*>(data);
- result.dims = dims;
- result.params = {ScaleFromMinMax<uint8_t>(min, max),
- ZeroPointFromMinMax<uint8_t>(min, max)};
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(uint8_t);
- result.is_variable = false;
- return result;
- }
- TfLiteTensor CreateQuantizedTensor(const int8_t* data, TfLiteIntArray* dims,
- float min, float max, bool is_variable) {
- TfLiteTensor result;
- result.type = kTfLiteInt8;
- result.data.int8 = const_cast<int8_t*>(data);
- result.dims = dims;
- result.params = {ScaleFromMinMax<int8_t>(min, max),
- ZeroPointFromMinMax<int8_t>(min, max)};
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(int8_t);
- result.is_variable = is_variable;
- return result;
- }
- TfLiteTensor CreateQuantizedTensor(float* data, uint8_t* quantized_data,
- TfLiteIntArray* dims, bool is_variable) {
- TfLiteTensor result;
- SymmetricQuantize(data, dims, quantized_data, &result.params.scale);
- result.data.uint8 = quantized_data;
- result.type = kTfLiteUInt8;
- result.dims = dims;
- result.params.zero_point = 128;
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(uint8_t);
- result.is_variable = is_variable;
- return result;
- }
- TfLiteTensor CreateQuantizedTensor(float* data, int8_t* quantized_data,
- TfLiteIntArray* dims, bool is_variable) {
- TfLiteTensor result;
- SignedSymmetricQuantize(data, dims, quantized_data, &result.params.scale);
- result.data.int8 = quantized_data;
- result.type = kTfLiteInt8;
- result.dims = dims;
- result.params.zero_point = 0;
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(int8_t);
- result.is_variable = is_variable;
- return result;
- }
- TfLiteTensor CreateQuantizedTensor(float* data, int16_t* quantized_data,
- TfLiteIntArray* dims, bool is_variable) {
- TfLiteTensor result;
- SignedSymmetricQuantize(data, dims, quantized_data, &result.params.scale);
- result.data.i16 = quantized_data;
- result.type = kTfLiteInt16;
- result.dims = dims;
- result.params.zero_point = 0;
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(int16_t);
- result.is_variable = is_variable;
- return result;
- }
- TfLiteTensor CreateQuantized32Tensor(const int32_t* data, TfLiteIntArray* dims,
- float scale, bool is_variable) {
- TfLiteTensor result;
- result.type = kTfLiteInt32;
- result.data.i32 = const_cast<int32_t*>(data);
- result.dims = dims;
- // Quantized int32_t tensors always have a zero point of 0, since the range of
- // int32_t values is large, and because zero point costs extra cycles during
- // processing.
- result.params = {scale, 0};
- result.allocation_type = kTfLiteMemNone;
- result.bytes = ElementCount(*dims) * sizeof(int32_t);
- result.is_variable = is_variable;
- return result;
- }
- } // namespace testing
- } // namespace tflite
|