1 /* Copyright 2019 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 #ifndef TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_GREEDY_MEMORY_PLANNER_H_ 17 #define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_GREEDY_MEMORY_PLANNER_H_ 18 19 #include "tensorflow/lite/micro/compatibility.h" 20 #include "tensorflow/lite/micro/memory_planner/memory_planner.h" 21 22 namespace tflite { 23 24 constexpr int kOnlinePlannedBuffer = -1; 25 26 // A memory planner that uses a greedy algorithm to arrange buffers in memory 27 // to minimize the overall arena size needed. 28 // 29 // The algorithm works like this: 30 // - The client enters the buffer information through AddBuffer(). 31 // - When a function like GetOffsetForBuffer() is called, the 32 // CalculateOffsetsIfNeeded() method is invoked. 33 // - If an up to date plan is not already present, one will be calculated. 34 // - The buffers are sorted in descending order of size. 35 // - The largest buffer is placed at offset zero. 36 // - The rest of the buffers are looped through in descending size order. 37 // - The other buffers that need to be in memory at the same time are found. 38 // - The first gap between simultaneously active buffers that the current 39 // buffer fits into will be used. 40 // - If no large-enough gap is found, the current buffer is placed after the 41 // last buffer that's simultaneously active. 42 // - This continues until all buffers are placed, and the offsets stored. 43 // 44 // This is not guaranteed to produce the best placement, since that's an 45 // NP-Complete problem, but in practice it should produce one that's decent. 46 class GreedyMemoryPlanner : public MemoryPlanner { 47 public: 48 // You need to pass in an area of memory to be used for planning. This memory 49 // needs to have a lifetime as long as the planner, but isn't owned by this 50 // object, so management should be handled by the client. This is so it can be 51 // stack or globally allocated if necessary on devices without dynamic memory 52 // allocation. How many buffers can be planned for will depend on the size of 53 // this scratch memory, so you should enlarge it if you see an error when 54 // calling AddBuffer(). The memory can be reused once you're done with the 55 // planner, as long as you copy the calculated offsets to another location. 56 // Each buffer requires about 36 bytes of scratch. 57 GreedyMemoryPlanner(unsigned char* scratch_buffer, int scratch_buffer_size); 58 ~GreedyMemoryPlanner() override; 59 60 // Record details of a buffer we want to place. 61 TfLiteStatus AddBuffer(ErrorReporter* error_reporter, int size, 62 int first_time_used, int last_time_used) override; 63 64 // Record details of an offline planned buffer offset we want to place. 65 // offline_offset is the buffer offset from the start of the arena. 66 TfLiteStatus AddBuffer(ErrorReporter* error_reporter, int size, 67 int first_time_used, int last_time_used, 68 int offline_offset); 69 70 // Returns the high-water mark of used memory. This is the minimum size of a 71 // memory arena you'd need to allocate to hold these buffers. 72 size_t GetMaximumMemorySize() override; 73 74 // How many buffers have been recorded. 75 int GetBufferCount() override; 76 77 // Where a given buffer should be placed in the memory arena. 78 // This information is stored in the memory arena itself, so once the arena 79 // is used for inference, it will be overwritten. 80 TfLiteStatus GetOffsetForBuffer(ErrorReporter* error_reporter, 81 int buffer_index, int* offset) override; 82 83 // Prints an ascii-art diagram of the buffer layout plan. 84 void PrintMemoryPlan(); 85 86 // Debug method to check whether any buffer allocations are overlapping. This 87 // is an O(N^2) complexity operation, so only use for testing. 88 bool DoAnyBuffersOverlap(ErrorReporter* error_reporter); 89 90 // Used to store a list of buffers ordered by their offset. 91 struct ListEntry { 92 int offset; 93 int requirements_index; 94 int next_entry_index; 95 }; 96 97 // Number of bytes required in order to plan a buffer. per_buffer_size()98 static size_t per_buffer_size() { 99 const int per_buffer_size = 100 sizeof(BufferRequirements) + // requirements_ 101 sizeof(int) + // buffer_sizes_sorted_ 102 sizeof(int) + // buffer_ids_sorted_ 103 sizeof(ListEntry) + // buffers_sorted_by_offset_ 104 sizeof(int); // buffer_offsets_; 105 return per_buffer_size; 106 } 107 108 private: 109 // Whether a buffer is active in a given time range. 110 bool DoesEntryOverlapInTime(const ListEntry* entry, const int first_time_used, 111 const int last_time_used) const; 112 113 // Walks the list to return the next buffer that is active in a given time 114 // range, or a null pointer if there are none. 115 ListEntry* NextSimultaneouslyActiveBuffer(const ListEntry* start, 116 const int first_time_used, 117 const int last_time_used); 118 119 // If there isn't an up to date plan, calculate a new one. 120 void CalculateOffsetsIfNeeded(); 121 122 // How many buffers we can plan for, based on the arena size we're given in 123 // the constructor. 124 int max_buffer_count_; 125 126 // The number of buffers added so far. 127 int buffer_count_; 128 129 // Records the client-provided information about each buffer. 130 struct BufferRequirements { 131 int size; 132 int offline_offset; 133 int first_time_used; 134 int last_time_used; 135 }; 136 137 // Working arrays used during the layout algorithm. 138 BufferRequirements* requirements_; 139 // buffer_sizes_sorted_ and buffer_ids_sorted_ are sorted according to: 140 // { 141 // offline planned buffers, 142 // online planned buffers sorted by size 143 // } 144 int* buffer_sizes_sorted_; 145 int* buffer_ids_sorted_; 146 ListEntry* buffers_sorted_by_offset_; 147 int next_free_entry_; // Index of the next free entry of 148 // buffers_sorted_by_offset_ 149 int first_entry_index_; // Index of the first entry (smallest offset) of 150 // buffers_sorted_by_offset_ 151 152 // Stores the outcome of the plan, the location of each buffer in the arena. 153 int* buffer_offsets_; 154 155 // Whether buffers have been added since the last plan was calculated. 156 bool need_to_calculate_offsets_; 157 158 TF_LITE_REMOVE_VIRTUAL_DELETE 159 }; 160 161 } // namespace tflite 162 163 #endif // TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_GREEDY_MEMORY_PLANNER_H_ 164