1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/lite/micro/micro_allocator.h"
17
18 #include <cstddef>
19 #include <cstdint>
20
21 #include "flatbuffers/flatbuffers.h" // from @flatbuffers
22 #include "tensorflow/lite/c/common.h"
23 #include "tensorflow/lite/core/api/error_reporter.h"
24 #include "tensorflow/lite/core/api/flatbuffer_conversions.h"
25 #include "tensorflow/lite/core/api/op_resolver.h"
26 #include "tensorflow/lite/core/api/tensor_utils.h"
27 #include "tensorflow/lite/kernels/internal/compatibility.h"
28 #include "tensorflow/lite/micro/compatibility.h"
29 #include "tensorflow/lite/micro/memory_helpers.h"
30 #include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h"
31 #include "tensorflow/lite/micro/memory_planner/memory_planner.h"
32 #include "tensorflow/lite/micro/micro_error_reporter.h"
33 #include "tensorflow/lite/micro/simple_memory_allocator.h"
34 #include "tensorflow/lite/schema/schema_generated.h"
35 #include "tensorflow/lite/schema/schema_utils.h"
36
37 namespace tflite {
38
39 namespace {
40
41 // Maximum number of scratch buffer requests per operator. Operator kernels that
42 // request more than this value will receive an exception.
43 constexpr size_t kMaxScratchBuffersPerOp = 12;
44
45 // Sentinel value used as a placeholder to mark a ScratchBufferRequest request
46 // needs a node id assignment.
47 constexpr int kUnassignedScratchBufferRequestIndex = -1;
48
49 // Used to hold information used during allocation calculations.
50 struct AllocationInfo {
51 size_t bytes;
52 void** output_ptr;
53 int first_created;
54 int last_used;
55 int32_t offline_offset;
56 bool needs_allocating;
57 };
58
59 // We align tensor buffers to 16-byte boundaries, since this is a common
60 // requirement for SIMD extensions.
61 constexpr int kBufferAlignment = 16;
62 constexpr char kOfflineMemAllocMetadata[] = "OfflineMemoryAllocation";
63 const TfLiteIntArray kZeroLengthIntArray = {};
64
65 class MicroBuiltinDataAllocator : public BuiltinDataAllocator {
66 public:
MicroBuiltinDataAllocator(SimpleMemoryAllocator * memory_allocator)67 explicit MicroBuiltinDataAllocator(SimpleMemoryAllocator* memory_allocator)
68 : memory_allocator_(memory_allocator) {}
69
Allocate(size_t size,size_t alignment_hint)70 void* Allocate(size_t size, size_t alignment_hint) override {
71 return memory_allocator_->AllocateFromTail(size, alignment_hint);
72 }
Deallocate(void * data)73 void Deallocate(void* data) override {
74 // Do not deallocate, builtin data needs to be available for the life time
75 // of the model.
76 }
77
78 private:
79 SimpleMemoryAllocator* memory_allocator_;
80
81 TF_LITE_REMOVE_VIRTUAL_DELETE
82 };
83
84 #if !defined(__clang__)
85 // Helper function to check flatbuffer metadata correctness. This function is
86 // not called by default. Hence it's not linked in to the final binary code.
CheckOfflinePlannedOffsets(const Model * model,ErrorReporter * error_reporter)87 TfLiteStatus CheckOfflinePlannedOffsets(const Model* model,
88 ErrorReporter* error_reporter) {
89 // Suppress compile warning for unused function
90 (void)CheckOfflinePlannedOffsets;
91
92 if (model->metadata()) {
93 for (size_t i = 0; i < model->metadata()->size(); ++i) {
94 auto metadata = model->metadata()->Get(i);
95 if (strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata,
96 strlen(kOfflineMemAllocMetadata)) == 0) {
97 auto* subgraphs = model->subgraphs();
98 const SubGraph* subgraph = (*subgraphs)[0];
99 const flatbuffers::Vector<flatbuffers::Offset<Tensor>>* tensors =
100 subgraph->tensors();
101 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers =
102 model->buffers();
103 int nbr_tflite_tensors = tensors->size();
104 auto* buffer = (*buffers)[metadata->buffer()];
105 auto* array = buffer->data();
106 const uint32_t* metadata_buffer = (uint32_t*)array->data();
107 int version = metadata_buffer[0];
108 int subgraph_idx = metadata_buffer[1];
109 const int nbr_offline_offsets = metadata_buffer[2];
110 #ifndef TF_LITE_STRIP_ERROR_STRINGS
111 int* offline_planner_offsets = (int*)&metadata_buffer[3];
112 #endif
113
114 TF_LITE_REPORT_ERROR(error_reporter, "==== Model metadata info: =====");
115 TF_LITE_REPORT_ERROR(error_reporter,
116 "Offline planner metadata found, version %d, "
117 "subgraph %d, nbr offline offsets %d",
118 version, subgraph_idx, nbr_offline_offsets);
119 for (int j = 0; j < nbr_offline_offsets; ++j) {
120 TF_LITE_REPORT_ERROR(
121 error_reporter,
122 "Offline planner tensor index %d, offline offset: %d", j,
123 offline_planner_offsets[j]);
124 }
125
126 if (version != 1) {
127 TF_LITE_REPORT_ERROR(error_reporter, "Version not supported! (%d)\n",
128 version);
129 return kTfLiteError;
130 }
131 if (subgraph_idx != 0) {
132 TF_LITE_REPORT_ERROR(error_reporter,
133 "Only 1 subgraph supported! Subgraph idx (%d)\n",
134 subgraph_idx);
135 return kTfLiteError;
136 }
137 if (nbr_tflite_tensors != nbr_offline_offsets) {
138 TF_LITE_REPORT_ERROR(error_reporter,
139 "Nbr of offline buffer offsets (%d) in metadata "
140 "not equal nbr tensors (%d)\n",
141 nbr_offline_offsets, nbr_tflite_tensors);
142 return kTfLiteError;
143 }
144 }
145 }
146 }
147 return kTfLiteOk;
148 }
149 #endif
150
151 // A helper class to construct AllocationInfo array. This array contains the
152 // lifetime of tensors / scratch_buffer and will be used to calculate the memory
153 // plan. Methods need to be called in order from `Init`, `Add*`, to `Finish`.
154 class AllocationInfoBuilder {
155 public:
AllocationInfoBuilder(AllocationInfo * info,size_t tensor_count,size_t scratch_buffer_count,ErrorReporter * reporter)156 AllocationInfoBuilder(AllocationInfo* info, size_t tensor_count,
157 size_t scratch_buffer_count, ErrorReporter* reporter)
158 : info_(info),
159 tensor_count_(tensor_count),
160 buffer_count_(scratch_buffer_count),
161 reporter_(reporter) {}
162
163 // Check if model contains offline planned buffer offsets.
164 // - If there's no metadata available, offline_planner_offsets is not set
165 // - If there's metadata available, offline_planner_offsets will point to the
166 // first offset in the metadata buffer list.
167 TfLiteStatus GetOfflinePlannedOffsets(
168 const Model* model, const int32_t** offline_planner_offsets);
169
170 // Add allocaiton information for the tensors.
171 TfLiteStatus AddTensors(const SubGraph* subgraph,
172 const int32_t* offline_offsets,
173 TfLiteEvalTensor* eval_tensors);
174
175 // Add allocation information for the scratch buffers.
176 TfLiteStatus AddScratchBuffers(
177 internal::ScratchBufferRequest* scratch_buffer_requests,
178 ScratchBufferHandle* scratch_buffer_handles);
179
180 // Returns a pointer to the built AllocationInfo array.
Finish() const181 const AllocationInfo* Finish() const { return info_; }
182
183 private:
184 AllocationInfo* info_ = nullptr;
185 size_t tensor_count_ = 0;
186 size_t buffer_count_ = 0;
187 ErrorReporter* reporter_ = nullptr;
188 };
189
AddTensors(const SubGraph * subgraph,const int32_t * offline_offsets,TfLiteEvalTensor * eval_tensors)190 TfLiteStatus AllocationInfoBuilder::AddTensors(const SubGraph* subgraph,
191 const int32_t* offline_offsets,
192 TfLiteEvalTensor* eval_tensors) {
193 TFLITE_DCHECK(eval_tensors != nullptr);
194
195 // Set up allocation info for all tensors.
196 for (size_t i = 0; i < tensor_count_; ++i) {
197 AllocationInfo* current = &info_[i];
198 current->output_ptr = &(eval_tensors[i].data.data);
199
200 TF_LITE_ENSURE_STATUS(
201 TfLiteEvalTensorByteLength(&eval_tensors[i], ¤t->bytes));
202
203 current->first_created = -1;
204 current->last_used = -1;
205 current->needs_allocating = (eval_tensors[i].data.data == nullptr) &&
206 (!subgraph->tensors()->Get(i)->is_variable());
207 if (offline_offsets) {
208 current->offline_offset = offline_offsets[i];
209 } else {
210 current->offline_offset = kOnlinePlannedBuffer;
211 }
212 }
213
214 uint32_t operators_size = NumSubgraphOperators(subgraph);
215
216 for (size_t i = 0; i < subgraph->inputs()->size(); ++i) {
217 const int tensor_index = subgraph->inputs()->Get(i);
218 AllocationInfo* current = &info_[tensor_index];
219 current->first_created = 0;
220 }
221
222 // Mark all outputs as persistent to the end of the invocation.
223 for (size_t i = 0; i < subgraph->outputs()->size(); ++i) {
224 const int tensor_index = subgraph->outputs()->Get(i);
225 AllocationInfo* current = &info_[tensor_index];
226 current->last_used = operators_size - 1;
227 }
228
229 // Figure out when the first and last use of each tensor is.
230 for (int i = (operators_size - 1); i >= 0; --i) {
231 const auto* op = subgraph->operators()->Get(i);
232 for (size_t n = 0; n < op->inputs()->size(); ++n) {
233 const int tensor_index = op->inputs()->Get(n);
234 AllocationInfo* current = &info_[tensor_index];
235 if (((current->last_used == -1) || (current->last_used < i))) {
236 current->last_used = i;
237 }
238 }
239 for (size_t n = 0; n < op->outputs()->size(); ++n) {
240 const int tensor_index = op->outputs()->Get(n);
241 AllocationInfo* current = &info_[tensor_index];
242 if ((current->first_created == -1) || (current->first_created > i)) {
243 current->first_created = i;
244 }
245 }
246 }
247 return kTfLiteOk;
248 }
249
250 // Get offline tensors allocation plan. See
251 // micro/docs/memory_management.md for more info.
GetOfflinePlannedOffsets(const Model * model,const int32_t ** offline_planner_offsets)252 TfLiteStatus AllocationInfoBuilder::GetOfflinePlannedOffsets(
253 const Model* model, const int32_t** offline_planner_offsets) {
254 if (model->metadata()) {
255 for (size_t i = 0; i < model->metadata()->size(); ++i) {
256 auto metadata = model->metadata()->Get(i);
257 if (strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata,
258 strlen(kOfflineMemAllocMetadata)) == 0) {
259 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers =
260 model->buffers();
261 auto* buffer = (*buffers)[metadata->buffer()];
262 auto* array = buffer->data();
263 const uint32_t* metadata_buffer =
264 reinterpret_cast<const uint32_t*>(array->data());
265 const size_t nbr_tensors = static_cast<size_t>(metadata_buffer[2]);
266 *offline_planner_offsets =
267 reinterpret_cast<const int32_t*>(&metadata_buffer[3]);
268
269 if (tensor_count_ != nbr_tensors) {
270 TF_LITE_REPORT_ERROR(reporter_,
271 "Nbr of offline buffer offsets (%d) in metadata "
272 "not equal nbr tensors (%d)\n",
273 nbr_tensors, tensor_count_);
274 return kTfLiteError;
275 }
276 }
277 }
278 }
279 return kTfLiteOk;
280 }
281
AddScratchBuffers(internal::ScratchBufferRequest * scratch_buffer_requests,ScratchBufferHandle * scratch_buffer_handles)282 TfLiteStatus AllocationInfoBuilder::AddScratchBuffers(
283 internal::ScratchBufferRequest* scratch_buffer_requests,
284 ScratchBufferHandle* scratch_buffer_handles) {
285 // Set up allocation info for buffers.
286 for (size_t i = tensor_count_; i < tensor_count_ + buffer_count_; ++i) {
287 internal::ScratchBufferRequest* current_request =
288 &(scratch_buffer_requests[i - tensor_count_]);
289 ScratchBufferHandle* current_handle =
290 &(scratch_buffer_handles[i - tensor_count_]);
291
292 AllocationInfo* current = &info_[i];
293 current->output_ptr = reinterpret_cast<void**>(¤t_handle->data);
294 current->bytes = current_request->bytes;
295 current->first_created = current_request->node_idx;
296 current->last_used = current_request->node_idx;
297 current->offline_offset = kOnlinePlannedBuffer;
298 current->needs_allocating = true;
299 }
300 return kTfLiteOk;
301 }
302
CreatePlan(ErrorReporter * error_reporter,GreedyMemoryPlanner * planner,const AllocationInfo * allocation_info,size_t allocation_info_size)303 TfLiteStatus CreatePlan(ErrorReporter* error_reporter,
304 GreedyMemoryPlanner* planner,
305 const AllocationInfo* allocation_info,
306 size_t allocation_info_size) {
307 // Add the tensors to our allocation plan.
308 for (size_t i = 0; i < allocation_info_size; ++i) {
309 const AllocationInfo* current = &allocation_info[i];
310 if (current->needs_allocating) {
311 size_t aligned_bytes_required =
312 AlignSizeUp(current->bytes, kBufferAlignment);
313 if (current->offline_offset == kOnlinePlannedBuffer) {
314 TF_LITE_ENSURE_STATUS(
315 planner->AddBuffer(error_reporter, aligned_bytes_required,
316 current->first_created, current->last_used));
317 } else {
318 TF_LITE_ENSURE_STATUS(planner->AddBuffer(
319 error_reporter, aligned_bytes_required, current->first_created,
320 current->last_used, current->offline_offset));
321 }
322 }
323 }
324 return kTfLiteOk;
325 }
326
CommitPlan(ErrorReporter * error_reporter,MemoryPlanner * planner,uint8_t * starting_point,const AllocationInfo * allocation_info,size_t allocation_info_size)327 TfLiteStatus CommitPlan(ErrorReporter* error_reporter, MemoryPlanner* planner,
328 uint8_t* starting_point,
329 const AllocationInfo* allocation_info,
330 size_t allocation_info_size) {
331 // Figure out the actual memory addresses for each buffer, based on the plan.
332 int planner_index = 0;
333 for (size_t i = 0; i < allocation_info_size; ++i) {
334 const AllocationInfo* current = &allocation_info[i];
335 if (current->needs_allocating) {
336 int offset = -1;
337 TF_LITE_ENSURE_STATUS(
338 planner->GetOffsetForBuffer(error_reporter, planner_index, &offset));
339 *current->output_ptr = reinterpret_cast<void*>(starting_point + offset);
340 ++planner_index;
341 }
342 }
343 return kTfLiteOk;
344 }
345 } // namespace
346
347 namespace internal {
348
349 // Handles architecture safe mapping of flatbuffer vectors to a TfLite*Array
350 // struct. Matching types are required (e.g. float and TfLiteFloatArray).
351 // Big-endian systems will always allocate dimension array data in the tail
352 // (persistent) section.
353 template <typename kFlatBufferVectorType, typename kTfLiteArrayType>
FlatBufferVectorToTfLiteTypeArray(SimpleMemoryAllocator * allocator,ErrorReporter * error_reporter,const flatbuffers::Vector<kFlatBufferVectorType> * flatbuffer_array,kTfLiteArrayType ** result)354 TfLiteStatus FlatBufferVectorToTfLiteTypeArray(
355 SimpleMemoryAllocator* allocator, ErrorReporter* error_reporter,
356 const flatbuffers::Vector<kFlatBufferVectorType>* flatbuffer_array,
357 kTfLiteArrayType** result) {
358 TFLITE_DCHECK(error_reporter != nullptr);
359 TFLITE_DCHECK(flatbuffer_array != nullptr);
360 // TODO(b/159668691): Consider adding type assertion or breaking this function
361 // into multiple functions for each type. std::is_same is c++11 and has a
362 // special updated constructor in c++17 that requires a string argument.
363 if (FLATBUFFERS_LITTLEENDIAN) {
364 // On little-endian machines, TfLite*Array happens to have the same memory
365 // layout as flatbuffers:Vector<kFlatBufferVectorType>, so we can
366 // reinterpret_cast the flatbuffer vector and avoid a copy and malloc.
367 *result = const_cast<kTfLiteArrayType*>(
368 reinterpret_cast<const kTfLiteArrayType*>(flatbuffer_array));
369 } else {
370 // Big-endian architecture can not use the same memory layout as
371 // flatbuffers::Vector<kFlatBufferVectorType>. Allocate from the tail and
372 // copy values from the flatbuffer into the newly allocated chunk.
373 kTfLiteArrayType* array = reinterpret_cast<kTfLiteArrayType*>(
374 allocator->SimpleMemoryAllocator::AllocateFromTail(
375 TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size()),
376 alignof(kTfLiteArrayType)));
377 if (array == nullptr) {
378 TF_LITE_REPORT_ERROR(
379 error_reporter,
380 "Failed to allocate %d bytes of memory to copy an array.",
381 TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size()));
382 return kTfLiteError;
383 }
384 array->size = flatbuffer_array->size();
385 for (int i = 0; i < array->size; ++i) {
386 array->data[i] = flatbuffer_array->Get(i);
387 }
388 *result = array;
389 }
390 return kTfLiteOk;
391 }
392
393 // Returns a pointer to any buffer associated with the flatbuffer tensor. Can
394 // return nullptr if no buffer is found.
GetFlatbufferTensorBuffer(const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers)395 void* GetFlatbufferTensorBuffer(
396 const tflite::Tensor& flatbuffer_tensor,
397 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers) {
398 // We need to figure out where the actual contents of this tensor are stored
399 // in memory. We'll check to see if there's a serialized buffer (pretty much
400 // the same as a constant op in TensorFlow) associated with this tensor first,
401 // and if there is update the runtime structure to point to its location in
402 // memory.
403 // First see if there's any buffer information in the serialized tensor.
404 // TODO(b/170379532): Add better unit tests to validate flatbuffer values.
405 void* out_buffer = nullptr;
406 if (auto* buffer = (*buffers)[flatbuffer_tensor.buffer()]) {
407 // If we've found a buffer, does it have any data?
408 if (auto* array = buffer->data()) {
409 // If it has any data, is the data size larger than zero?
410 if (array->size()) {
411 // We've found a buffer with valid data, so update the runtime tensor
412 // data structure to point to it.
413 out_buffer = const_cast<void*>(static_cast<const void*>(array->data()));
414 }
415 }
416 // TODO(petewarden): It's not clear in what circumstances we could have a
417 // buffer in the serialized tensor, but it doesn't have any data in it. Is
418 // that a validly-generated file, and if so what does it mean, or is it an
419 // error condition? It would be good to tighten up the specification to make
420 // it less ambiguous.
421 }
422 return out_buffer;
423 }
424
InitializeTfLiteTensorFromFlatbuffer(SimpleMemoryAllocator * allocator,bool allocate_temp,const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers,ErrorReporter * error_reporter,TfLiteTensor * result)425 TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
426 SimpleMemoryAllocator* allocator, bool allocate_temp,
427 const tflite::Tensor& flatbuffer_tensor,
428 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
429 ErrorReporter* error_reporter, TfLiteTensor* result) {
430 TFLITE_DCHECK(result != nullptr);
431
432 *result = {};
433 // Make sure the serialized type is one we know how to deal with, and convert
434 // it from a flatbuffer enum into a constant used by the kernel C API.
435 TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
436 &result->type, error_reporter));
437 // Make sure we remember if the serialized tensor is designated as a variable.
438 result->is_variable = flatbuffer_tensor.is_variable();
439
440 result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
441
442 // TODO(petewarden): Some of these paths aren't getting enough testing
443 // coverage, so we should figure out some tests that exercise them.
444 if (result->data.data == nullptr) {
445 // The tensor contents haven't been set from a serialized buffer, so
446 // make a note that they will be allocated from memory. The actual
447 // allocation won't happen until later.
448 result->allocation_type = kTfLiteArenaRw;
449 } else {
450 // We set the data from a serialized buffer, so record tha.
451 result->allocation_type = kTfLiteMmapRo;
452 }
453
454 // Figure out what the size in bytes of the buffer is and store it.
455 size_t type_size;
456 TF_LITE_ENSURE_STATUS(BytesRequiredForTensor(
457 flatbuffer_tensor, &result->bytes, &type_size, error_reporter));
458
459 if (flatbuffer_tensor.shape() == nullptr) {
460 // flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
461 // tensor.
462 result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
463 } else {
464 // TFLM doesn't allow reshaping the tensor which requires dynamic memory
465 // allocation so it is safe to drop the const qualifier. In the future, if
466 // we really want to update the tensor shape, we can always pass in a new
467 // TfLiteIntArray - especially we have to do so if the dimension is
468 TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
469 allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
470 }
471
472 // Copy the quantization information from the serialized data.
473 const auto* src_quantization = flatbuffer_tensor.quantization();
474 if (src_quantization && src_quantization->scale() &&
475 (src_quantization->scale()->size() > 0) &&
476 src_quantization->zero_point() &&
477 (src_quantization->zero_point()->size() > 0)) {
478 // Always populate the TfLiteTensor.params field, even if there are
479 // per-channel quantization parameters.
480 result->params.scale = src_quantization->scale()->Get(0);
481 // Note that the zero_point field in the FlatBuffers schema is a 64-bit
482 // integer, but the zero_point field in the TfLiteQuantizationParams struct
483 // is a 32-bit integer.
484 result->params.zero_point =
485 static_cast<int32_t>(src_quantization->zero_point()->Get(0));
486
487 // Populate per-channel quantization params.
488 int channels = src_quantization->scale()->size();
489 TfLiteAffineQuantization* quantization =
490 allocate_temp
491 ? reinterpret_cast<TfLiteAffineQuantization*>(
492 allocator->AllocateTemp(sizeof(TfLiteAffineQuantization),
493 alignof(TfLiteAffineQuantization)))
494 : reinterpret_cast<TfLiteAffineQuantization*>(
495 allocator->AllocateFromTail(
496 sizeof(TfLiteAffineQuantization),
497 alignof(TfLiteAffineQuantization)));
498 if (quantization == nullptr) {
499 TF_LITE_REPORT_ERROR(error_reporter,
500 "Unable to allocate TfLiteAffineQuantization.\n");
501 return kTfLiteError;
502 }
503
504 // TODO(b/153688719): Reduce tail allocation by using a global zero-point
505 // buffer. This value can not be reused from the flatbuffer since the
506 // zero_point is stored as a int64_t.
507 quantization->zero_point =
508 allocate_temp
509 ? reinterpret_cast<TfLiteIntArray*>(allocator->AllocateTemp(
510 TfLiteIntArrayGetSizeInBytes(channels),
511 alignof(TfLiteIntArray)))
512 : reinterpret_cast<TfLiteIntArray*>(allocator->AllocateFromTail(
513 TfLiteIntArrayGetSizeInBytes(channels),
514 alignof(TfLiteIntArray)));
515 if (quantization->zero_point == nullptr) {
516 TF_LITE_REPORT_ERROR(error_reporter,
517 "Unable to allocate quantization->zero_point.\n");
518 return kTfLiteError;
519 }
520
521 TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
522 allocator, error_reporter, src_quantization->scale(),
523 &quantization->scale));
524
525 quantization->zero_point->size = channels;
526 int* zero_point_data = quantization->zero_point->data;
527 for (int i = 0; i < channels; i++) {
528 zero_point_data[i] = src_quantization->zero_point()->Get(i);
529 }
530 // TODO(rocky): Need to add a micro_allocator test case that fails when
531 // this is not copied:
532 quantization->quantized_dimension = src_quantization->quantized_dimension();
533
534 result->quantization = {kTfLiteAffineQuantization, quantization};
535 }
536 return kTfLiteOk;
537 }
538
InitializeTfLiteEvalTensorFromFlatbuffer(SimpleMemoryAllocator * allocator,const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers,ErrorReporter * error_reporter,TfLiteEvalTensor * result)539 TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer(
540 SimpleMemoryAllocator* allocator, const tflite::Tensor& flatbuffer_tensor,
541 const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
542 ErrorReporter* error_reporter, TfLiteEvalTensor* result) {
543 *result = {};
544 // Make sure the serialized type is one we know how to deal with, and convert
545 // it from a flatbuffer enum into a constant used by the kernel C API.
546 TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
547 &result->type, error_reporter));
548
549 result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
550
551 if (flatbuffer_tensor.shape() == nullptr) {
552 // flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
553 // tensor.
554 result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
555 } else {
556 TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
557 allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
558 }
559 return kTfLiteOk;
560 }
561
562 } // namespace internal
563
MicroAllocator(SimpleMemoryAllocator * memory_allocator,ErrorReporter * error_reporter)564 MicroAllocator::MicroAllocator(SimpleMemoryAllocator* memory_allocator,
565 ErrorReporter* error_reporter)
566 : memory_allocator_(memory_allocator),
567 error_reporter_(error_reporter),
568 model_is_allocating_(false) {}
569
~MicroAllocator()570 MicroAllocator::~MicroAllocator() {}
571
Create(uint8_t * tensor_arena,size_t arena_size,ErrorReporter * error_reporter)572 MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size,
573 ErrorReporter* error_reporter) {
574 uint8_t* aligned_arena = AlignPointerUp(tensor_arena, kBufferAlignment);
575 size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena;
576 return Create(SimpleMemoryAllocator::Create(error_reporter, aligned_arena,
577 aligned_arena_size),
578 error_reporter);
579 }
580
Create(SimpleMemoryAllocator * memory_allocator,ErrorReporter * error_reporter)581 MicroAllocator* MicroAllocator::Create(SimpleMemoryAllocator* memory_allocator,
582 ErrorReporter* error_reporter) {
583 TFLITE_DCHECK(memory_allocator != nullptr);
584 TFLITE_DCHECK(error_reporter != nullptr);
585
586 uint8_t* allocator_buffer = memory_allocator->AllocateFromTail(
587 sizeof(MicroAllocator), alignof(MicroAllocator));
588 MicroAllocator* allocator =
589 new (allocator_buffer) MicroAllocator(memory_allocator, error_reporter);
590 return allocator;
591 }
592
StartModelAllocation(const Model * model)593 SubgraphAllocations* MicroAllocator::StartModelAllocation(const Model* model) {
594 TFLITE_DCHECK(model != nullptr);
595
596 if (model_is_allocating_) {
597 TF_LITE_REPORT_ERROR(error_reporter_,
598 "MicroAllocator: Model allocation started before "
599 "finishing previously allocated model");
600 return nullptr;
601 }
602
603 model_is_allocating_ = true;
604
605 uint8_t* data_allocator_buffer = memory_allocator_->AllocateFromTail(
606 sizeof(MicroBuiltinDataAllocator), alignof(MicroBuiltinDataAllocator));
607 builtin_data_allocator_ =
608 new (data_allocator_buffer) MicroBuiltinDataAllocator(memory_allocator_);
609
610 if (InitScratchBufferData() != kTfLiteOk) {
611 return nullptr;
612 }
613
614 // Allocate struct to store eval tensors, nodes and registrations.
615 SubgraphAllocations* output = reinterpret_cast<SubgraphAllocations*>(
616 memory_allocator_->AllocateFromTail(
617 sizeof(SubgraphAllocations) * model->subgraphs()->size(),
618 alignof(SubgraphAllocations)));
619 if (output == nullptr) {
620 MicroPrintf("Failed to allocate memory for model metadata.");
621 return nullptr;
622 }
623
624 if (AllocateTfLiteEvalTensors(model, output) != kTfLiteOk ||
625 AllocateNodeAndRegistrations(model, output) != kTfLiteOk) {
626 return nullptr;
627 }
628 return output;
629 }
630
FinishModelAllocation(const Model * model,SubgraphAllocations * subgraph_allocations,ScratchBufferHandle ** scratch_buffer_handles)631 TfLiteStatus MicroAllocator::FinishModelAllocation(
632 const Model* model, SubgraphAllocations* subgraph_allocations,
633 ScratchBufferHandle** scratch_buffer_handles) {
634 if (!model_is_allocating_) {
635 TF_LITE_REPORT_ERROR(error_reporter_,
636 "MicroAllocator: Model allocation finished before "
637 "starting allocating model");
638 return kTfLiteError;
639 }
640
641 // TODO(b/187993197): Track scratch buffers for each subgraph.
642 for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
643 subgraph_idx++) {
644 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
645 TFLITE_DCHECK(subgraph != nullptr);
646
647 TF_LITE_ENSURE_STATUS(AllocateScratchBufferHandles(
648 scratch_buffer_handles, scratch_buffer_request_count_));
649 TF_LITE_ENSURE_STATUS(CommitStaticMemoryPlan(
650 model, subgraph_allocations[subgraph_idx].tensors,
651 *scratch_buffer_handles, subgraph_idx));
652 TF_LITE_ENSURE_STATUS(AllocateVariables(
653 subgraph, subgraph_allocations[subgraph_idx].tensors));
654 }
655 model_is_allocating_ = false;
656 return kTfLiteOk;
657 }
658
AllocatePersistentBuffer(size_t bytes)659 void* MicroAllocator::AllocatePersistentBuffer(size_t bytes) {
660 return memory_allocator_->AllocateFromTail(bytes, kBufferAlignment);
661 }
662
RequestScratchBufferInArena(size_t bytes,int subgraph_idx,int * buffer_idx)663 TfLiteStatus MicroAllocator::RequestScratchBufferInArena(size_t bytes,
664 int subgraph_idx,
665 int* buffer_idx) {
666 // All scratch buffer requests are stored in the head section of the arena
667 // when a model is in the prepare phase. First align a scratch buffer request
668 // pointer to the start of the head:
669 internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
670
671 // Count the number of requested scratch buffers for the current node:
672 size_t current_node_request_count = 0;
673 for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
674 if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
675 ++current_node_request_count;
676 }
677 }
678
679 // First, ensure that the per-kernel request has not exceeded the limit:
680 if (current_node_request_count >= kMaxScratchBuffersPerOp) {
681 TF_LITE_REPORT_ERROR(
682 error_reporter_,
683 "Scratch buffer request exeeds limit per operator (%d)",
684 kMaxScratchBuffersPerOp);
685 return kTfLiteError;
686 }
687
688 // Initialize and assign values for the request at the current index:
689 internal::ScratchBufferRequest* current_request =
690 &requests[scratch_buffer_request_count_];
691 *current_request = {};
692 // Assign -1 as a sentinel value that will be updated when the node finishes
693 // allocating:
694 current_request->bytes = bytes;
695 current_request->node_idx = kUnassignedScratchBufferRequestIndex;
696
697 // Assign the current request index to the out-param:
698 *buffer_idx = scratch_buffer_request_count_;
699
700 // Bump the request count to prepare for the next request:
701 ++scratch_buffer_request_count_;
702 return kTfLiteOk;
703 }
704
FinishPrepareNodeAllocations(int node_id)705 TfLiteStatus MicroAllocator::FinishPrepareNodeAllocations(int node_id) {
706 // When a node has finished preparing, all temp allocations performed by the
707 // kernel should be cleaned up:
708 ResetTempAllocations();
709
710 // Find and update any new scratch buffer requests for the current node:
711 internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
712
713 for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
714 // A request with a node_idx of -1 is a sentinel value used to indicate this
715 // was a new request for the current node. The allocator finally knows the
716 // node index at this point. Assign the value and update the list of new
717 // requests so the head section can be adjusted to allow for the next kernel
718 // to allocate at most kMaxScratchBuffersPerOp requests:
719 if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
720 requests[i].node_idx = node_id;
721 }
722 }
723
724 // Ensure that the head is re-adjusted to allow for another at-most
725 // kMaxScratchBuffersPerOp scratch buffer requests in the next operator:
726 TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
727 sizeof(internal::ScratchBufferRequest) *
728 (scratch_buffer_request_count_ + kMaxScratchBuffersPerOp),
729 alignof(internal::ScratchBufferRequest)));
730
731 return kTfLiteOk;
732 }
733
used_bytes() const734 size_t MicroAllocator::used_bytes() const {
735 return memory_allocator_->GetUsedBytes();
736 }
737
AllocateNodeAndRegistrations(const Model * model,SubgraphAllocations * subgraph_allocations)738 TfLiteStatus MicroAllocator::AllocateNodeAndRegistrations(
739 const Model* model, SubgraphAllocations* subgraph_allocations) {
740 TFLITE_DCHECK(subgraph_allocations != nullptr);
741
742 for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
743 subgraph_idx++) {
744 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
745 TFLITE_DCHECK(subgraph != nullptr);
746
747 uint32_t operators_size = NumSubgraphOperators(subgraph);
748
749 // Initialize NodeAndRegistrations for the subgraph.
750 NodeAndRegistration* output = reinterpret_cast<NodeAndRegistration*>(
751 memory_allocator_->AllocateFromTail(
752 sizeof(NodeAndRegistration) * operators_size,
753 alignof(NodeAndRegistration)));
754 if (output == nullptr) {
755 TF_LITE_REPORT_ERROR(
756 error_reporter_,
757 "Failed to allocate memory for node_and_registrations.");
758 return kTfLiteError;
759 }
760 subgraph_allocations[subgraph_idx].node_and_registrations = output;
761 }
762 return kTfLiteOk;
763 }
AllocatePersistentTfLiteTensor(const Model * model,const SubgraphAllocations * subgraph_allocations,int tensor_index,int subgraph_index)764 TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensor(
765 const Model* model, const SubgraphAllocations* subgraph_allocations,
766 int tensor_index, int subgraph_index) {
767 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
768 TFLITE_DCHECK(subgraph != nullptr);
769
770 // This value is allocated from persistent arena space. It is guaranteed to be
771 // around for the lifetime of the application.
772 TfLiteTensor* tensor = AllocatePersistentTfLiteTensorInternal();
773
774 // Populate any fields from the flatbuffer, since this TfLiteTensor struct is
775 // allocated in the persistent section of the arena, ensure that additional
776 // allocations also take place in that section of the arena.
777 if (PopulateTfLiteTensorFromFlatbuffer(
778 model, tensor, tensor_index, subgraph_index,
779 /*allocate_temp=*/false) != kTfLiteOk) {
780 TF_LITE_REPORT_ERROR(error_reporter_,
781 "Failed to populate a persistent TfLiteTensor struct "
782 "from flatbuffer data!");
783 return nullptr;
784 }
785
786 if (subgraph_allocations != nullptr) {
787 // Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
788 // and not located in the flatbuffer are stored on the pre-allocated list of
789 // TfLiteEvalTensors structs. These structs are the source of truth, simply
790 // point the corresponding buffer to the new TfLiteTensor data value.
791 tensor->data.data =
792 subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
793 // TfLiteEvalTensor structs must also be the source of truth for the
794 // TfLiteTensor dims.
795 tensor->dims =
796 subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
797 }
798 return tensor;
799 }
800
AllocateTempTfLiteTensor(const Model * model,const SubgraphAllocations * subgraph_allocations,int tensor_index,int subgraph_index)801 TfLiteTensor* MicroAllocator::AllocateTempTfLiteTensor(
802 const Model* model, const SubgraphAllocations* subgraph_allocations,
803 int tensor_index, int subgraph_index) {
804 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
805 TFLITE_DCHECK(subgraph != nullptr);
806
807 // This value is allocated from temporary arena space. It is guaranteed to be
808 // around for at least the scope of the calling function. Since this struct
809 // allocation takes place in temp space, no need to own or cleanup.
810 TfLiteTensor* tensor =
811 reinterpret_cast<TfLiteTensor*>(memory_allocator_->AllocateTemp(
812 sizeof(TfLiteTensor), alignof(TfLiteTensor)));
813
814 // Populate any fields from the flatbuffer, since this TfLiteTensor struct is
815 // allocated in the temp section of the arena, ensure that additional
816 // allocations also take place in that section of the arena.
817 if (PopulateTfLiteTensorFromFlatbuffer(model, tensor, tensor_index,
818 subgraph_index,
819 /*allocate_temp=*/true) != kTfLiteOk) {
820 TF_LITE_REPORT_ERROR(
821 error_reporter_,
822 "Failed to populate a temp TfLiteTensor struct from flatbuffer data!");
823 return nullptr;
824 }
825
826 if (subgraph_allocations != nullptr) {
827 // Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
828 // and not located in the flatbuffer are stored on the pre-allocated list of
829 // TfLiteEvalTensors structs. These structs are the source of truth, simply
830 // point the corresponding buffer to the new TfLiteTensor data value.
831 tensor->data.data =
832 subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
833 // TfLiteEvalTensor structs must also be the source of truth for the
834 // TfLiteTensor dims.
835 tensor->dims =
836 subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
837 }
838 return tensor;
839 }
840
ResetTempAllocations()841 void MicroAllocator::ResetTempAllocations() {
842 memory_allocator_->ResetTempAllocations();
843 }
844
AllocateTfLiteEvalTensors(const Model * model,SubgraphAllocations * subgraph_allocations)845 TfLiteStatus MicroAllocator::AllocateTfLiteEvalTensors(
846 const Model* model, SubgraphAllocations* subgraph_allocations) {
847 TFLITE_DCHECK(subgraph_allocations != nullptr);
848
849 for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
850 subgraph_idx++) {
851 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
852 TFLITE_DCHECK(subgraph != nullptr);
853
854 size_t alloc_count = subgraph->tensors()->size();
855 TfLiteEvalTensor* tensors =
856 reinterpret_cast<TfLiteEvalTensor*>(memory_allocator_->AllocateFromTail(
857 sizeof(TfLiteEvalTensor) * alloc_count, alignof(TfLiteEvalTensor)));
858 if (tensors == nullptr) {
859 TF_LITE_REPORT_ERROR(
860 error_reporter_,
861 "Failed to allocate memory for context->eval_tensors, "
862 "%d bytes required",
863 sizeof(TfLiteEvalTensor) * alloc_count);
864 return kTfLiteError;
865 }
866
867 for (size_t i = 0; i < alloc_count; ++i) {
868 TfLiteStatus status = internal::InitializeTfLiteEvalTensorFromFlatbuffer(
869 memory_allocator_, *subgraph->tensors()->Get(i), model->buffers(),
870 error_reporter_, &tensors[i]);
871 if (status != kTfLiteOk) {
872 TF_LITE_REPORT_ERROR(error_reporter_, "Failed to initialize tensor %d",
873 i);
874 return kTfLiteError;
875 }
876 }
877 subgraph_allocations[subgraph_idx].tensors = tensors;
878 }
879 return kTfLiteOk;
880 }
AllocateVariables(const SubGraph * subgraph,TfLiteEvalTensor * eval_tensors)881 TfLiteStatus MicroAllocator::AllocateVariables(const SubGraph* subgraph,
882 TfLiteEvalTensor* eval_tensors) {
883 for (size_t i = 0; i < subgraph->tensors()->size(); ++i) {
884 auto* tensor = subgraph->tensors()->Get(i);
885 if (tensor->is_variable()) {
886 size_t buffer_size;
887 TF_LITE_ENSURE_STATUS(
888 TfLiteEvalTensorByteLength(&eval_tensors[i], &buffer_size));
889
890 eval_tensors[i].data.data =
891 memory_allocator_->AllocateFromTail(buffer_size, kBufferAlignment);
892
893 if (eval_tensors[i].data.data == nullptr) {
894 TF_LITE_REPORT_ERROR(error_reporter_,
895 "Failed to allocate variable tensor of size %d",
896 buffer_size);
897 return kTfLiteError;
898 }
899 }
900 }
901 return kTfLiteOk;
902 }
903
AllocatePersistentTfLiteTensorInternal()904 TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensorInternal() {
905 return reinterpret_cast<TfLiteTensor*>(memory_allocator_->AllocateFromTail(
906 sizeof(TfLiteTensor), alignof(TfLiteTensor)));
907 }
908
PopulateTfLiteTensorFromFlatbuffer(const Model * model,TfLiteTensor * tensor,int tensor_index,int subgraph_idx,bool allocate_temp)909 TfLiteStatus MicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
910 const Model* model, TfLiteTensor* tensor, int tensor_index,
911 int subgraph_idx, bool allocate_temp) {
912 // TODO(b/162311891): This method serves as a stub to ensure quantized
913 // allocations in the tail can be recorded. Once the interpreter has APIs for
914 // accessing buffers on TfLiteEvalTensor this method can be dropped.
915 return internal::InitializeTfLiteTensorFromFlatbuffer(
916 memory_allocator_, allocate_temp,
917 *model->subgraphs()->Get(subgraph_idx)->tensors()->Get(tensor_index),
918 model->buffers(), error_reporter_, tensor);
919 }
920
error_reporter() const921 ErrorReporter* MicroAllocator::error_reporter() const {
922 return error_reporter_;
923 }
924
CommitStaticMemoryPlan(const Model * model,TfLiteEvalTensor * eval_tensors,ScratchBufferHandle * scratch_buffer_handles,int subgraph_idx)925 TfLiteStatus MicroAllocator::CommitStaticMemoryPlan(
926 const Model* model, TfLiteEvalTensor* eval_tensors,
927 ScratchBufferHandle* scratch_buffer_handles, int subgraph_idx) {
928 size_t head_usage = 0;
929 // Create static memory plan
930 // 1. Calculate AllocationInfo to know the lifetime of each tensor/buffer.
931 // 2. Add them into the planner (such as the GreedyMemoryPlanner).
932 // 3. Static memory planning using the planner.
933 // 4. Set tensor/buffer pointers based on the offsets from the previous step.
934 //
935 // Note that AllocationInfo is only needed for creating the plan. It will be
936 // allocated from the temp section and cleaned up at the bottom of this
937 // function.
938
939 const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
940 size_t allocation_info_count =
941 subgraph->tensors()->size() + scratch_buffer_request_count_;
942 size_t bytes = sizeof(AllocationInfo) * allocation_info_count;
943
944 // Allocate an array of AllocationInfo structs from the temp section. This
945 // struct will be used by AllocationInfoBuilder to find buffer usage.
946 AllocationInfo* allocation_info = reinterpret_cast<AllocationInfo*>(
947 memory_allocator_->AllocateTemp(bytes, alignof(AllocationInfo)));
948 if (allocation_info == nullptr) {
949 TF_LITE_REPORT_ERROR(
950 error_reporter_,
951 "Failed to allocate memory for allocation_info, %d bytes required",
952 bytes);
953 return kTfLiteError;
954 }
955
956 // Use the AllocationInfoBuilder class to help determine where buffers are
957 // used in the subgraph.
958 AllocationInfoBuilder builder(allocation_info, subgraph->tensors()->size(),
959 scratch_buffer_request_count_, error_reporter_);
960
961 const int32_t* offline_planner_offsets = nullptr;
962 TF_LITE_ENSURE_STATUS(
963 builder.GetOfflinePlannedOffsets(model, &offline_planner_offsets));
964 TF_LITE_ENSURE_STATUS(
965 builder.AddTensors(subgraph, offline_planner_offsets, eval_tensors));
966
967 internal::ScratchBufferRequest* scratch_buffer_requests =
968 GetScratchBufferRequests();
969
970 TF_LITE_ENSURE_STATUS(builder.AddScratchBuffers(scratch_buffer_requests,
971 scratch_buffer_handles));
972
973 // Remaining arena size that memory planner can use for calculating offsets.
974 size_t remaining_arena_size =
975 memory_allocator_->GetAvailableMemory(kBufferAlignment);
976 uint8_t* planner_arena =
977 memory_allocator_->AllocateTemp(remaining_arena_size, kBufferAlignment);
978 TF_LITE_ENSURE(error_reporter_, planner_arena != nullptr);
979 GreedyMemoryPlanner planner(planner_arena, remaining_arena_size);
980 TF_LITE_ENSURE_STATUS(CreatePlan(error_reporter_, &planner, allocation_info,
981 allocation_info_count));
982
983 // Reset all temp allocations used above:
984 memory_allocator_->ResetTempAllocations();
985
986 size_t actual_available_arena_size =
987 memory_allocator_->GetAvailableMemory(kBufferAlignment);
988
989 // Make sure we have enough arena size.
990 if (planner.GetMaximumMemorySize() > actual_available_arena_size) {
991 TF_LITE_REPORT_ERROR(
992 error_reporter_,
993 "Arena size is too small for all buffers. Needed %u but only "
994 "%u was available.",
995 planner.GetMaximumMemorySize(), actual_available_arena_size);
996 return kTfLiteError;
997 }
998 // Commit the plan.
999 TF_LITE_ENSURE_STATUS(CommitPlan(error_reporter_, &planner,
1000 memory_allocator_->GetHeadBuffer(),
1001 allocation_info, allocation_info_count));
1002 #ifdef TF_LITE_SHOW_MEMORY_USE
1003 planner.PrintMemoryPlan();
1004 #endif
1005 head_usage = planner.GetMaximumMemorySize();
1006
1007 // The head is used to store memory plans for one model at a time during the
1008 // model preparation stage, and is re-purposed to store scratch buffer handles
1009 // during model invocation. The head must be as large as the greater of the
1010 // largest model memory plan's size and the total space required for all
1011 // scratch buffer handles.
1012 if (max_head_buffer_usage_ < head_usage) {
1013 max_head_buffer_usage_ = head_usage;
1014 }
1015
1016 // The head is used for storing scratch buffer allocations before finalizing a
1017 // memory plan in this function. Ensure that the head is set to the largest
1018 // memory plan sent through the allocator:
1019 TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
1020 max_head_buffer_usage_, kBufferAlignment));
1021 return kTfLiteOk;
1022 }
1023
AllocateScratchBufferHandles(ScratchBufferHandle ** scratch_buffer_handles,size_t handle_count)1024 TfLiteStatus MicroAllocator::AllocateScratchBufferHandles(
1025 ScratchBufferHandle** scratch_buffer_handles, size_t handle_count) {
1026 TFLITE_DCHECK(scratch_buffer_handles != nullptr);
1027
1028 if (scratch_buffer_request_count_ == 0) {
1029 // No scratch buffer requests were requested during model allocation.
1030 return kTfLiteOk;
1031 }
1032
1033 // Allocate a consecutive block of memory store the scratch buffer handles.
1034 // This alignment ensures quick lookup during inference time for the model:
1035 *scratch_buffer_handles = reinterpret_cast<ScratchBufferHandle*>(
1036 memory_allocator_->AllocateFromTail(
1037 sizeof(ScratchBufferHandle) * handle_count,
1038 alignof(ScratchBufferHandle)));
1039
1040 return kTfLiteOk;
1041 }
1042
InitScratchBufferData()1043 TfLiteStatus MicroAllocator::InitScratchBufferData() {
1044 // A model is preparing to allocate resources, ensure that scratch buffer
1045 // request counter is cleared:
1046 scratch_buffer_request_count_ = 0;
1047
1048 // All requests will be stored in the head section. Each kernel is allowed at
1049 // most kMaxScratchBuffersPerOp requests. Adjust the head to reserve at most
1050 // that many requests to begin:
1051 TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
1052 sizeof(internal::ScratchBufferRequest) * kMaxScratchBuffersPerOp,
1053 alignof(internal::ScratchBufferRequest)));
1054
1055 return kTfLiteOk;
1056 }
1057
GetScratchBufferRequests()1058 internal::ScratchBufferRequest* MicroAllocator::GetScratchBufferRequests() {
1059 return reinterpret_cast<internal::ScratchBufferRequest*>(
1060 AlignPointerUp(memory_allocator_->GetHeadBuffer(),
1061 alignof(internal::ScratchBufferRequest)));
1062 }
1063
FlatBufferVectorToTfLiteTypeArray(const flatbuffers::Vector<int32_t> * flatbuffer_array,TfLiteIntArray ** result)1064 TfLiteStatus MicroAllocator::FlatBufferVectorToTfLiteTypeArray(
1065 const flatbuffers::Vector<int32_t>* flatbuffer_array,
1066 TfLiteIntArray** result) {
1067 return internal::FlatBufferVectorToTfLiteTypeArray(
1068 memory_allocator_, error_reporter_, flatbuffer_array, result);
1069 }
1070
GetBuiltinDataAllocator()1071 BuiltinDataAllocator* MicroAllocator::GetBuiltinDataAllocator() {
1072 return builtin_data_allocator_;
1073 }
1074
1075 } // namespace tflite
1076