| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_allocator.h" |
|
|
| #include <cstddef> |
| #include <cstdint> |
|
|
| #include "edge-impulse-sdk/third_party/flatbuffers/include/flatbuffers/flatbuffers.h" |
| #include "edge-impulse-sdk/tensorflow/lite/c/common.h" |
| #include "edge-impulse-sdk/tensorflow/lite/kernels/internal/compatibility.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/non_persistent_arena_buffer_allocator.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/persistent_arena_buffer_allocator.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/single_arena_buffer_allocator.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/compatibility.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/flatbuffer_utils.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/memory_helpers.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/memory_planner/micro_memory_planner.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_allocation_info.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_arena_constants.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_log.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/flatbuffer_conversions_bridge.h" |
| #include "edge-impulse-sdk/tensorflow/lite/schema/schema_generated.h" |
| #include "edge-impulse-sdk/tensorflow/lite/schema/schema_utils.h" |
|
|
| namespace tflite { |
|
|
| namespace { |
|
|
| |
| |
| constexpr size_t kMaxScratchBuffersPerOp = 12; |
|
|
| |
| |
| constexpr int kUnassignedScratchBufferRequestIndex = -1; |
|
|
| const TfLiteIntArray kZeroLengthIntArray = {}; |
|
|
| class MicroBuiltinDataAllocator : public TfLiteBridgeBuiltinDataAllocator { |
| public: |
| explicit MicroBuiltinDataAllocator( |
| IPersistentBufferAllocator* persistent_allocator) |
| : persistent_allocator_(persistent_allocator) {} |
|
|
| void* Allocate(size_t size, size_t alignment_hint) override { |
| return persistent_allocator_->AllocatePersistentBuffer(size, |
| alignment_hint); |
| } |
| void Deallocate(void* data) override { |
| |
| |
| } |
|
|
| TF_LITE_REMOVE_VIRTUAL_DELETE |
|
|
| private: |
| IPersistentBufferAllocator* persistent_allocator_; |
| }; |
|
|
| TfLiteStatus CreatePlan(MicroMemoryPlanner* planner, |
| const AllocationInfo* allocation_info, |
| size_t allocation_info_size) { |
| |
| for (size_t i = 0; i < allocation_info_size; ++i) { |
| const AllocationInfo* current = &allocation_info[i]; |
| if (current->needs_allocating) { |
| size_t aligned_bytes_required = |
| AlignSizeUp(current->bytes, MicroArenaBufferAlignment()); |
| if (current->offline_offset == kOnlinePlannedBuffer) { |
| TF_LITE_ENSURE_STATUS(planner->AddBuffer(aligned_bytes_required, |
| current->first_created, |
| current->last_used)); |
| } else { |
| TF_LITE_ENSURE_STATUS( |
| planner->AddBuffer(aligned_bytes_required, current->first_created, |
| current->last_used, current->offline_offset)); |
| } |
| } |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus CommitPlan(MicroMemoryPlanner* planner, uint8_t* starting_point, |
| const AllocationInfo* allocation_info, |
| size_t allocation_info_size) { |
| |
| int planner_index = 0; |
| for (size_t i = 0; i < allocation_info_size; ++i) { |
| const AllocationInfo* current = &allocation_info[i]; |
| if (current->needs_allocating) { |
| int offset = -1; |
| TF_LITE_ENSURE_STATUS( |
| planner->GetOffsetForBuffer(planner_index, &offset)); |
| *current->output_ptr = reinterpret_cast<void*>(starting_point + offset); |
| ++planner_index; |
| } |
| } |
| return kTfLiteOk; |
| } |
|
|
| IPersistentBufferAllocator* CreatePersistentArenaAllocator(uint8_t* buffer_head, |
| size_t buffer_size) { |
| |
| |
| uint8_t* aligned_buffer_tail = |
| AlignPointerDown(buffer_head + buffer_size, MicroArenaBufferAlignment()); |
| size_t aligned_buffer_size = aligned_buffer_tail - buffer_head; |
| PersistentArenaBufferAllocator tmp = |
| PersistentArenaBufferAllocator(buffer_head, aligned_buffer_size); |
|
|
| |
| |
| |
| uint8_t* allocator_buffer = |
| tmp.AllocatePersistentBuffer(sizeof(PersistentArenaBufferAllocator), |
| alignof(PersistentArenaBufferAllocator)); |
| |
| return new (allocator_buffer) PersistentArenaBufferAllocator(tmp); |
| } |
|
|
| |
| |
| |
| INonPersistentBufferAllocator* CreateNonPersistentArenaAllocator( |
| uint8_t* buffer_head, size_t buffer_size, |
| IPersistentBufferAllocator* persistent_buffer_allocator) { |
| uint8_t* allocator_buffer = |
| persistent_buffer_allocator->AllocatePersistentBuffer( |
| sizeof(NonPersistentArenaBufferAllocator), |
| alignof(NonPersistentArenaBufferAllocator)); |
| |
| |
| uint8_t* aligned_buffer_head = |
| AlignPointerUp(buffer_head, MicroArenaBufferAlignment()); |
| size_t aligned_buffer_size = buffer_head + buffer_size - aligned_buffer_head; |
|
|
| INonPersistentBufferAllocator* non_persistent_buffer_allocator = |
| new (allocator_buffer) NonPersistentArenaBufferAllocator( |
| aligned_buffer_head, aligned_buffer_size); |
| return non_persistent_buffer_allocator; |
| } |
|
|
| } |
|
|
| namespace internal { |
|
|
| |
| |
| void* GetFlatbufferTensorBuffer( |
| const tflite::Tensor& flatbuffer_tensor, |
| const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers) { |
| |
| |
| |
| |
| |
| |
| |
| void* out_buffer = nullptr; |
| if (auto* buffer = (*buffers)[flatbuffer_tensor.buffer()]) { |
| |
| if (auto* array = buffer->data()) { |
| |
| if (array->size()) { |
| |
| |
| out_buffer = const_cast<void*>(static_cast<const void*>(array->data())); |
| } |
| } |
| |
| |
| |
| |
| |
| } |
| return out_buffer; |
| } |
|
|
| TfLiteStatus InitializeTfLiteTensorFromFlatbuffer( |
| IPersistentBufferAllocator* persistent_buffer_allocator, |
| INonPersistentBufferAllocator* non_persistent_buffer_allocator, |
| bool allocate_temp, const tflite::Tensor& flatbuffer_tensor, |
| const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers, |
| TfLiteTensor* result) { |
| TFLITE_DCHECK(result != nullptr); |
|
|
| *result = {}; |
| |
| |
| TF_LITE_ENSURE_STATUS( |
| tflite::ConvertTensorType(flatbuffer_tensor.type(), &result->type)); |
| |
| result->is_variable = flatbuffer_tensor.is_variable(); |
|
|
| result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers); |
| |
| #if defined(EI_LOG_LEVEL) && (EI_LOG_LEVEL >= 4) |
| result->name = flatbuffer_tensor.name()->c_str(); |
| #endif |
| |
| |
| if (result->data.data == nullptr) { |
| |
| |
| |
| result->allocation_type = kTfLiteArenaRw; |
| } else { |
| |
| result->allocation_type = kTfLiteMmapRo; |
| } |
|
|
| |
| size_t type_size; |
| TF_LITE_ENSURE_STATUS( |
| BytesRequiredForTensor(flatbuffer_tensor, &result->bytes, &type_size)); |
|
|
| if (flatbuffer_tensor.shape() == nullptr) { |
| |
| |
| |
| result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray); |
| } else { |
| |
| |
| |
| |
| result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape()); |
| } |
|
|
| |
| const auto* src_quantization = flatbuffer_tensor.quantization(); |
| if (src_quantization && src_quantization->scale() && |
| (src_quantization->scale()->size() > 0) && |
| src_quantization->zero_point() && |
| (src_quantization->zero_point()->size() > 0)) { |
| |
| |
| result->params.scale = src_quantization->scale()->Get(0); |
| |
| |
| |
| result->params.zero_point = |
| static_cast<int32_t>(src_quantization->zero_point()->Get(0)); |
|
|
| |
| int channels = src_quantization->scale()->size(); |
| TfLiteAffineQuantization* quantization = |
| allocate_temp |
| ? reinterpret_cast<TfLiteAffineQuantization*>( |
| non_persistent_buffer_allocator->AllocateTemp( |
| sizeof(TfLiteAffineQuantization), |
| alignof(TfLiteAffineQuantization))) |
| : reinterpret_cast<TfLiteAffineQuantization*>( |
| persistent_buffer_allocator->AllocatePersistentBuffer( |
| sizeof(TfLiteAffineQuantization), |
| alignof(TfLiteAffineQuantization))); |
| if (quantization == nullptr) { |
| MicroPrintf("Unable to allocate TfLiteAffineQuantization.\n"); |
| return kTfLiteError; |
| } |
|
|
| |
| |
| |
| quantization->zero_point = |
| allocate_temp |
| ? reinterpret_cast<TfLiteIntArray*>( |
| non_persistent_buffer_allocator->AllocateTemp( |
| TfLiteIntArrayGetSizeInBytes(channels), |
| alignof(TfLiteIntArray))) |
| : reinterpret_cast<TfLiteIntArray*>( |
| persistent_buffer_allocator->AllocatePersistentBuffer( |
| TfLiteIntArrayGetSizeInBytes(channels), |
| alignof(TfLiteIntArray))); |
| if (quantization->zero_point == nullptr) { |
| MicroPrintf("Unable to allocate quantization->zero_point.\n"); |
| return kTfLiteError; |
| } |
|
|
| quantization->scale = |
| FlatBufferVectorToTfLiteTypeArray(src_quantization->scale()); |
|
|
| quantization->zero_point->size = channels; |
| int* zero_point_data = quantization->zero_point->data; |
| for (int i = 0; i < channels; i++) { |
| |
| |
| zero_point_data[i] = src_quantization->zero_point()->size() == |
| src_quantization->scale()->size() |
| ? src_quantization->zero_point()->Get(i) |
| : src_quantization->zero_point()->Get(0); |
| } |
| |
| |
| quantization->quantized_dimension = src_quantization->quantized_dimension(); |
|
|
| result->quantization = {kTfLiteAffineQuantization, quantization}; |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer( |
| const tflite::Tensor& flatbuffer_tensor, |
| const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers, |
| TfLiteEvalTensor* result) { |
| *result = {}; |
| |
| |
| TF_LITE_ENSURE_STATUS( |
| tflite::ConvertTensorType(flatbuffer_tensor.type(), &result->type)); |
|
|
| result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers); |
|
|
| if (flatbuffer_tensor.shape() == nullptr) { |
| |
| |
| result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray); |
| } else { |
| result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape()); |
| } |
| return kTfLiteOk; |
| } |
|
|
| } |
|
|
| size_t MicroAllocator::GetDefaultTailUsage(bool is_memory_planner_given) { |
| size_t total_size = AlignSizeUp<SingleArenaBufferAllocator>() + |
| AlignSizeUp<MicroAllocator>() + |
| AlignSizeUp<MicroBuiltinDataAllocator>() + |
| AlignSizeUp<SubgraphAllocations>(); |
| if (!is_memory_planner_given) { |
| total_size += AlignSizeUp<GreedyMemoryPlanner>(); |
| } |
| return total_size; |
| } |
|
|
| MicroAllocator::MicroAllocator(SingleArenaBufferAllocator* memory_allocator, |
| MicroMemoryPlanner* memory_planner) |
| : non_persistent_buffer_allocator_(memory_allocator), |
| persistent_buffer_allocator_(memory_allocator), |
| memory_planner_(memory_planner), |
| model_is_allocating_(false) {} |
|
|
| MicroAllocator::MicroAllocator( |
| IPersistentBufferAllocator* persistent_buffer_allocator, |
| INonPersistentBufferAllocator* non_persistent_buffer_allocator, |
| MicroMemoryPlanner* memory_planner) |
| : non_persistent_buffer_allocator_(non_persistent_buffer_allocator), |
| persistent_buffer_allocator_(persistent_buffer_allocator), |
| memory_planner_(memory_planner), |
| model_is_allocating_(false) {} |
|
|
| MicroAllocator::~MicroAllocator() {} |
|
|
| MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size, |
| MicroMemoryPlanner* memory_planner) { |
| uint8_t* aligned_arena = |
| AlignPointerUp(tensor_arena, MicroArenaBufferAlignment()); |
| size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena; |
| SingleArenaBufferAllocator* memory_allocator = |
| SingleArenaBufferAllocator::Create(aligned_arena, aligned_arena_size); |
|
|
| return Create(memory_allocator, memory_planner); |
| } |
|
|
| MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, |
| size_t arena_size) { |
| uint8_t* aligned_arena = |
| AlignPointerUp(tensor_arena, MicroArenaBufferAlignment()); |
| size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena; |
| SingleArenaBufferAllocator* memory_allocator = |
| SingleArenaBufferAllocator::Create(aligned_arena, aligned_arena_size); |
|
|
| |
| |
| uint8_t* memory_planner_buffer = memory_allocator->AllocatePersistentBuffer( |
| sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner)); |
| GreedyMemoryPlanner* memory_planner = |
| new (memory_planner_buffer) GreedyMemoryPlanner(); |
|
|
| return Create(memory_allocator, memory_planner); |
| } |
|
|
| MicroAllocator* MicroAllocator::Create( |
| SingleArenaBufferAllocator* memory_allocator, |
| MicroMemoryPlanner* memory_planner) { |
| TFLITE_DCHECK(memory_allocator != nullptr); |
| TFLITE_DCHECK(memory_planner != nullptr); |
|
|
| uint8_t* allocator_buffer = memory_allocator->AllocatePersistentBuffer( |
| sizeof(MicroAllocator), alignof(MicroAllocator)); |
| MicroAllocator* allocator = new (allocator_buffer) |
| MicroAllocator(memory_allocator, memory_allocator, memory_planner); |
| return allocator; |
| } |
|
|
| MicroAllocator* MicroAllocator::Create(uint8_t* persistent_tensor_arena, |
| size_t persistent_arena_size, |
| uint8_t* non_persistent_tensor_arena, |
| size_t non_persistent_arena_size) { |
| TFLITE_DCHECK(persistent_tensor_arena != nullptr); |
| TFLITE_DCHECK(non_persistent_tensor_arena != nullptr); |
| TFLITE_DCHECK(persistent_tensor_arena != non_persistent_tensor_arena); |
|
|
| IPersistentBufferAllocator* persistent_buffer_allocator = |
| CreatePersistentArenaAllocator(persistent_tensor_arena, |
| persistent_arena_size); |
| INonPersistentBufferAllocator* non_persistent_buffer_allocator = |
| CreateNonPersistentArenaAllocator(non_persistent_tensor_arena, |
| non_persistent_arena_size, |
| persistent_buffer_allocator); |
|
|
| uint8_t* memory_planner_buffer = |
| persistent_buffer_allocator->AllocatePersistentBuffer( |
| sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner)); |
| GreedyMemoryPlanner* memory_planner = |
| new (memory_planner_buffer) GreedyMemoryPlanner(); |
|
|
| uint8_t* micro_allocator_buffer = |
| persistent_buffer_allocator->AllocatePersistentBuffer( |
| sizeof(MicroAllocator), alignof(MicroAllocator)); |
| MicroAllocator* allocator = new (micro_allocator_buffer) |
| MicroAllocator(persistent_buffer_allocator, |
| non_persistent_buffer_allocator, memory_planner); |
| return allocator; |
| } |
|
|
| SubgraphAllocations* MicroAllocator::StartModelAllocation(const Model* model) { |
| TFLITE_DCHECK(model != nullptr); |
|
|
| if (model_is_allocating_) { |
| MicroPrintf( |
| "MicroAllocator: Model allocation started before " |
| "finishing previously allocated model"); |
| return nullptr; |
| } |
|
|
| model_is_allocating_ = true; |
|
|
| uint8_t* data_allocator_buffer = |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(MicroBuiltinDataAllocator), |
| alignof(MicroBuiltinDataAllocator)); |
| builtin_data_allocator_ = new (data_allocator_buffer) |
| MicroBuiltinDataAllocator(persistent_buffer_allocator_); |
|
|
| if (InitScratchBufferData() != kTfLiteOk) { |
| return nullptr; |
| } |
|
|
| |
| SubgraphAllocations* output = reinterpret_cast<SubgraphAllocations*>( |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(SubgraphAllocations) * model->subgraphs()->size(), |
| alignof(SubgraphAllocations))); |
| if (output == nullptr) { |
| MicroPrintf("Failed to allocate memory for model metadata."); |
| return nullptr; |
| } |
|
|
| if (AllocateTfLiteEvalTensors(model, output) != kTfLiteOk || |
| AllocateNodeAndRegistrations(model, output) != kTfLiteOk) { |
| return nullptr; |
| } |
| return output; |
| } |
|
|
| TfLiteStatus MicroAllocator::FinishModelAllocation( |
| const Model* model, SubgraphAllocations* subgraph_allocations, |
| ScratchBufferHandle** scratch_buffer_handles) { |
| if (!model_is_allocating_) { |
| MicroPrintf( |
| "MicroAllocator: Model allocation finished before " |
| "starting allocating model"); |
| return kTfLiteError; |
| } |
|
|
| |
| TF_LITE_ENSURE_STATUS(AllocateScratchBufferHandles( |
| scratch_buffer_handles, scratch_buffer_request_count_)); |
|
|
| |
| TF_LITE_ENSURE_STATUS(CommitStaticMemoryPlan(model, subgraph_allocations, |
| *scratch_buffer_handles)); |
| model_is_allocating_ = false; |
| return kTfLiteOk; |
| } |
|
|
| void* MicroAllocator::AllocatePersistentBuffer(size_t bytes) { |
| return persistent_buffer_allocator_->AllocatePersistentBuffer( |
| bytes, MicroArenaBufferAlignment()); |
| } |
|
|
| TfLiteStatus MicroAllocator::RequestScratchBufferInArena(size_t bytes, |
| int subgraph_idx, |
| int* buffer_idx) { |
| |
| |
| |
| internal::ScratchBufferRequest* requests = GetScratchBufferRequests(); |
|
|
| |
| size_t current_node_request_count = 0; |
| for (size_t i = 0; i < scratch_buffer_request_count_; ++i) { |
| if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) { |
| ++current_node_request_count; |
| } |
| } |
|
|
| |
| if (current_node_request_count >= kMaxScratchBuffersPerOp) { |
| MicroPrintf("Scratch buffer request exeeds limit per operator (%d)", |
| kMaxScratchBuffersPerOp); |
| return kTfLiteError; |
| } |
|
|
| |
| internal::ScratchBufferRequest* current_request = |
| &requests[scratch_buffer_request_count_]; |
| *current_request = {}; |
| |
| |
| current_request->bytes = bytes; |
| current_request->node_idx = kUnassignedScratchBufferRequestIndex; |
| current_request->subgraph_idx = subgraph_idx; |
|
|
| |
| *buffer_idx = scratch_buffer_request_count_; |
|
|
| |
| ++scratch_buffer_request_count_; |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroAllocator::FinishPrepareNodeAllocations(int node_id) { |
| |
| |
| TF_LITE_ENSURE_STATUS(ResetTempAllocations()); |
|
|
| |
| internal::ScratchBufferRequest* requests = GetScratchBufferRequests(); |
|
|
| for (size_t i = 0; i < scratch_buffer_request_count_; ++i) { |
| |
| |
| |
| |
| |
| if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) { |
| requests[i].node_idx = node_id; |
| } |
| } |
|
|
| |
| |
| TF_LITE_ENSURE_STATUS(non_persistent_buffer_allocator_->ResizeBuffer( |
| scratch_buffer_head_, |
| sizeof(internal::ScratchBufferRequest) * |
| (scratch_buffer_request_count_ + kMaxScratchBuffersPerOp), |
| alignof(internal::ScratchBufferRequest))); |
|
|
| return kTfLiteOk; |
| } |
|
|
| size_t MicroAllocator::used_bytes() const { |
| return non_persistent_buffer_allocator_->GetNonPersistentUsedBytes() + |
| persistent_buffer_allocator_->GetPersistentUsedBytes(); |
| } |
|
|
| TfLiteStatus MicroAllocator::AllocateNodeAndRegistrations( |
| const Model* model, SubgraphAllocations* subgraph_allocations) { |
| TFLITE_DCHECK(subgraph_allocations != nullptr); |
|
|
| for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size(); |
| subgraph_idx++) { |
| const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx); |
| TFLITE_DCHECK(subgraph != nullptr); |
|
|
| uint32_t operators_size = NumSubgraphOperators(subgraph); |
|
|
| |
| NodeAndRegistration* output = reinterpret_cast<NodeAndRegistration*>( |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(NodeAndRegistration) * operators_size, |
| alignof(NodeAndRegistration))); |
| if (output == nullptr) { |
| MicroPrintf("Failed to allocate memory for node_and_registrations."); |
| return kTfLiteError; |
| } |
| subgraph_allocations[subgraph_idx].node_and_registrations = output; |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensor( |
| const Model* model, const SubgraphAllocations* subgraph_allocations, |
| int tensor_index, int subgraph_index) { |
| const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index); |
| TFLITE_DCHECK(subgraph != nullptr); |
|
|
| |
| |
| TfLiteTensor* tensor = AllocatePersistentTfLiteTensorInternal(); |
|
|
| |
| |
| |
| if (PopulateTfLiteTensorFromFlatbuffer( |
| model, tensor, tensor_index, subgraph_index, |
| false) != kTfLiteOk) { |
| MicroPrintf( |
| "Failed to populate a persistent TfLiteTensor struct " |
| "from flatbuffer data!"); |
| return nullptr; |
| } |
|
|
| if (subgraph_allocations != nullptr) { |
| |
| |
| |
| |
| tensor->data.data = |
| subgraph_allocations[subgraph_index].tensors[tensor_index].data.data; |
| |
| |
| tensor->dims = |
| subgraph_allocations[subgraph_index].tensors[tensor_index].dims; |
| } |
| return tensor; |
| } |
|
|
| void MicroAllocator::DeallocateTempTfLiteTensor(TfLiteTensor* tensor) { |
| TFLITE_DCHECK(tensor != nullptr); |
|
|
| if (tensor->quantization.type == kTfLiteAffineQuantization) { |
| TFLITE_DCHECK(tensor->quantization.params != nullptr); |
| TfLiteAffineQuantization* quantization = |
| reinterpret_cast<TfLiteAffineQuantization*>( |
| tensor->quantization.params); |
|
|
| non_persistent_buffer_allocator_->DeallocateTemp( |
| reinterpret_cast<uint8_t*>(quantization->zero_point)); |
| non_persistent_buffer_allocator_->DeallocateTemp( |
| reinterpret_cast<uint8_t*>(quantization)); |
| } |
|
|
| |
| tensor->quantization.type = kTfLiteNoQuantization; |
| tensor->quantization.params = nullptr; |
| tensor->data.data = nullptr; |
| tensor->dims = nullptr; |
| non_persistent_buffer_allocator_->DeallocateTemp( |
| reinterpret_cast<uint8_t*>(tensor)); |
| } |
|
|
| TfLiteTensor* MicroAllocator::AllocateTempTfLiteTensor( |
| const Model* model, const SubgraphAllocations* subgraph_allocations, |
| int tensor_index, int subgraph_index) { |
| const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index); |
| TFLITE_DCHECK(subgraph != nullptr); |
|
|
| |
| |
| |
| TfLiteTensor* tensor = reinterpret_cast<TfLiteTensor*>( |
| non_persistent_buffer_allocator_->AllocateTemp(sizeof(TfLiteTensor), |
| alignof(TfLiteTensor))); |
|
|
| |
| |
| |
| if (PopulateTfLiteTensorFromFlatbuffer(model, tensor, tensor_index, |
| subgraph_index, |
| true) != kTfLiteOk) { |
| MicroPrintf( |
| "Failed to populate a temp TfLiteTensor struct from flatbuffer data!"); |
| return nullptr; |
| } |
|
|
| if (subgraph_allocations != nullptr) { |
| |
| |
| |
| |
| tensor->data.data = |
| subgraph_allocations[subgraph_index].tensors[tensor_index].data.data; |
| |
| |
| tensor->dims = |
| subgraph_allocations[subgraph_index].tensors[tensor_index].dims; |
| } |
| return tensor; |
| } |
|
|
| TfLiteStatus MicroAllocator::ResetTempAllocations() { |
| return non_persistent_buffer_allocator_->ResetTempAllocations(); |
| } |
|
|
| bool MicroAllocator::IsAllTempDeallocated() { |
| return non_persistent_buffer_allocator_->IsAllTempDeallocated(); |
| } |
|
|
| TfLiteStatus MicroAllocator::AllocateTfLiteEvalTensors( |
| const Model* model, SubgraphAllocations* subgraph_allocations) { |
| TFLITE_DCHECK(subgraph_allocations != nullptr); |
|
|
| for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size(); |
| subgraph_idx++) { |
| const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx); |
| TFLITE_DCHECK(subgraph != nullptr); |
|
|
| size_t alloc_count = subgraph->tensors()->size(); |
| TfLiteEvalTensor* tensors = reinterpret_cast<TfLiteEvalTensor*>( |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(TfLiteEvalTensor) * alloc_count, alignof(TfLiteEvalTensor))); |
| if (tensors == nullptr) { |
| MicroPrintf( |
| "Failed to allocate memory for context->eval_tensors, " |
| "%d bytes required", |
| sizeof(TfLiteEvalTensor) * alloc_count); |
| return kTfLiteError; |
| } |
|
|
| for (size_t i = 0; i < alloc_count; ++i) { |
| TfLiteStatus status = internal::InitializeTfLiteEvalTensorFromFlatbuffer( |
| *subgraph->tensors()->Get(i), model->buffers(), &tensors[i]); |
| if (status != kTfLiteOk) { |
| MicroPrintf("Failed to initialize tensor %d", i); |
| return kTfLiteError; |
| } |
| } |
| subgraph_allocations[subgraph_idx].tensors = tensors; |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroAllocator::AllocateVariables( |
| const SubGraph* subgraph, TfLiteEvalTensor* eval_tensors, |
| const int32_t* offline_planner_offsets) { |
| for (size_t i = 0; i < subgraph->tensors()->size(); ++i) { |
| auto* tensor = subgraph->tensors()->Get(i); |
| if (tensor->is_variable()) { |
| if (offline_planner_offsets == nullptr || |
| offline_planner_offsets[i] == kOnlinePlannedBuffer) { |
| size_t buffer_size; |
| TF_LITE_ENSURE_STATUS( |
| TfLiteEvalTensorByteLength(&eval_tensors[i], &buffer_size)); |
|
|
| eval_tensors[i].data.data = |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| buffer_size, MicroArenaBufferAlignment()); |
|
|
| if (eval_tensors[i].data.data == nullptr) { |
| MicroPrintf("Failed to allocate variable tensor of size %d", |
| buffer_size); |
| return kTfLiteError; |
| } |
| } |
| } |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensorInternal() { |
| return reinterpret_cast<TfLiteTensor*>( |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(TfLiteTensor), alignof(TfLiteTensor))); |
| } |
|
|
| TfLiteStatus MicroAllocator::PopulateTfLiteTensorFromFlatbuffer( |
| const Model* model, TfLiteTensor* tensor, int tensor_index, |
| int subgraph_idx, bool allocate_temp) { |
| |
| |
| |
| return internal::InitializeTfLiteTensorFromFlatbuffer( |
| persistent_buffer_allocator_, non_persistent_buffer_allocator_, |
| allocate_temp, |
| *model->subgraphs()->Get(subgraph_idx)->tensors()->Get(tensor_index), |
| model->buffers(), tensor); |
| } |
|
|
| TfLiteStatus MicroAllocator::CommitStaticMemoryPlan( |
| const Model* model, SubgraphAllocations* allocations, |
| ScratchBufferHandle* scratch_buffer_handles) { |
| size_t head_usage = 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| AllocationInfoBuilder builder(model, non_persistent_buffer_allocator_); |
| TF_LITE_ENSURE_STATUS( |
| builder.CreateAllocationInfo(scratch_buffer_request_count_)); |
|
|
| const int32_t* offline_planner_offsets = nullptr; |
| TF_LITE_ENSURE_STATUS( |
| builder.GetOfflinePlannedOffsets(&offline_planner_offsets)); |
|
|
| |
| |
| for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size(); |
| subgraph_idx++) { |
| const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx); |
| TFLITE_DCHECK(subgraph != nullptr); |
| TF_LITE_ENSURE_STATUS(AllocateVariables( |
| subgraph, allocations[subgraph_idx].tensors, offline_planner_offsets)); |
| } |
|
|
| TF_LITE_ENSURE_STATUS( |
| builder.InitializeAllocationInfo(offline_planner_offsets, allocations)); |
|
|
| internal::ScratchBufferRequest* scratch_buffer_requests = |
| GetScratchBufferRequests(); |
| TF_LITE_ENSURE_STATUS(builder.MarkAllocationLifetimes( |
| 0, scratch_buffer_requests, scratch_buffer_handles, allocations)); |
| int allocation_info_count = builder.AllocationCount(); |
| AllocationInfo* allocation_info = builder.Finish(); |
|
|
| |
| size_t remaining_arena_size = |
| non_persistent_buffer_allocator_->GetAvailableMemory( |
| MicroArenaBufferAlignment()); |
| uint8_t* planner_arena = non_persistent_buffer_allocator_->AllocateTemp( |
| remaining_arena_size, MicroArenaBufferAlignment()); |
|
|
| if (planner_arena == nullptr) { |
| return kTfLiteError; |
| } |
|
|
| memory_planner_->Init(planner_arena, remaining_arena_size); |
| TF_LITE_ENSURE_STATUS( |
| CreatePlan(memory_planner_, allocation_info, allocation_info_count)); |
|
|
| |
| TF_LITE_ENSURE_STATUS( |
| CommitPlan(memory_planner_, |
| non_persistent_buffer_allocator_->GetOverlayMemoryAddress(), |
| allocation_info, allocation_info_count)); |
|
|
| |
| builder.FreeAllocationInfo(); |
| non_persistent_buffer_allocator_->DeallocateTemp(planner_arena); |
| TF_LITE_ENSURE_STATUS( |
| non_persistent_buffer_allocator_->ResetTempAllocations()); |
| TF_LITE_ENSURE_STATUS( |
| non_persistent_buffer_allocator_->DeallocateResizableBuffer( |
| scratch_buffer_head_)); |
|
|
| #ifdef TF_LITE_SHOW_MEMORY_USE |
| memory_planner_->PrintMemoryPlan(); |
| #endif |
| head_usage = memory_planner_->GetMaximumMemorySize(); |
|
|
| |
| |
| |
| |
| |
| if (max_head_buffer_usage_ < head_usage) { |
| max_head_buffer_usage_ = head_usage; |
| } |
|
|
| |
| |
| |
| TF_LITE_ENSURE_STATUS( |
| non_persistent_buffer_allocator_->ReserveNonPersistentOverlayMemory( |
| max_head_buffer_usage_, MicroArenaBufferAlignment())); |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroAllocator::AllocateScratchBufferHandles( |
| ScratchBufferHandle** scratch_buffer_handles, size_t handle_count) { |
| TFLITE_DCHECK(scratch_buffer_handles != nullptr); |
|
|
| if (scratch_buffer_request_count_ == 0) { |
| |
| return kTfLiteOk; |
| } |
|
|
| |
| |
| *scratch_buffer_handles = reinterpret_cast<ScratchBufferHandle*>( |
| persistent_buffer_allocator_->AllocatePersistentBuffer( |
| sizeof(ScratchBufferHandle) * handle_count, |
| alignof(ScratchBufferHandle))); |
|
|
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroAllocator::InitScratchBufferData() { |
| |
| |
| scratch_buffer_request_count_ = 0; |
|
|
| |
| |
| |
| scratch_buffer_head_ = |
| non_persistent_buffer_allocator_->AllocateResizableBuffer( |
| sizeof(internal::ScratchBufferRequest) * kMaxScratchBuffersPerOp, |
| alignof(internal::ScratchBufferRequest)); |
| if (scratch_buffer_head_ == nullptr) { |
| return kTfLiteError; |
| } |
|
|
| return kTfLiteOk; |
| } |
|
|
| internal::ScratchBufferRequest* MicroAllocator::GetScratchBufferRequests() { |
| return reinterpret_cast<internal::ScratchBufferRequest*>(AlignPointerUp( |
| scratch_buffer_head_, alignof(internal::ScratchBufferRequest))); |
| } |
|
|
| TfLiteBridgeBuiltinDataAllocator* MicroAllocator::GetBuiltinDataAllocator() { |
| return builtin_data_allocator_; |
| } |
|
|
| } |
|
|