ONE - On-device Neural Engine
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
MemoryPlanner.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "MemoryPlanner.h"
18#include "util/logging.h"
19#include <cassert>
20
22{
23
25{
26 Block blk{_capacity, size};
27 _mem_plans[ind] = blk;
28 _capacity += size;
29
30 VERBOSE(BP_PLANNER) << "CLAIM(" << ind << "): " << blk.offset << ", " << blk.size << std::endl;
31}
32
34{
35 VERBOSE(BP_PLANNER) << "RELEASE(" << ind << "): "
36 << "NOTHING does" << std::endl;
37}
38
39// There are some assumptions for claiming memory(== making a reservation for memory).
40// 1. About _claim_table(std::map).
41// - The table's data structure is std::map so that it always sorts
42// value(OperandIndex) by key(base_offset).
43// - This claim() inserts key/value into _claim_table and the release() removes the key/value from
44// _claim_table.
45// - _claim_table shows the memory status at a certain point in time. Therefore,
46// - If _claim_table has an offset and a certain size at a certain point in time,
47// it means the place at the offset has been already claimed(== can't claim now. need to find
48// someplace new).
49// - If _claim_table doesn't have any element for an offset and a certain size at a certain
50// point in time, it means the place at the offset can be claimed.
51// 2. In the loop for _claim_table, we can assume the current claim_base_offset value is bigger than
52// the previous claim_base_offset.
54{
55 // Find the right position for claiming
56 uint32_t next_offset = 0;
57 for (const auto &[claimed_base_offset, claimed_operand_idx] : _claim_table)
58 {
59 auto claimed_size = _mem_plans[claimed_operand_idx].size;
60 if (next_offset + size <= claimed_base_offset)
61 {
62 break;
63 }
64 else
65 {
66 next_offset = claimed_base_offset + claimed_size;
67 }
68 }
69
70 // Now next_offset is set to the proper offset
71 _claim_table[next_offset] = ind;
72 _mem_plans[ind] = {next_offset, size};
73
74 VERBOSE(FF_PLANNER) << "claim(" << ind << "): [+" << next_offset << ", " << size << "sz]"
75 << std::endl;
76
77 if (_capacity < next_offset + size)
78 {
79 _capacity = next_offset + size;
80 }
81}
82
84{
85 for (auto it = _claim_table.cbegin(); it != _claim_table.cend(); ++it)
86 {
87 if (it->second == ind)
88 {
89 uint32_t offset = it->first;
90 uint32_t index = ind.value();
91 uint32_t size = _mem_plans[ind].size;
92
93 _claim_table.erase(it);
94
95 VERBOSE(FF_PLANNER) << "release(" << index << "): [+" << offset << ", " << size << "sz]"
96 << std::endl;
97 return;
98 }
99 }
100 assert(!"Cannot release for given index. It has been not claimed or released already.");
101}
102
104 : _initialized(false), _capacity(0), _mem_plans(), _live_operands(), _interference_graph(),
105 _operands()
106{
107 // DO NOTHING
108}
109
111{
112 _operands.emplace(size, ind);
113 _interference_graph[ind].insert(_interference_graph[ind].end(), _live_operands.cbegin(),
114 _live_operands.cend());
115 for (const auto &live_operand : _live_operands)
116 {
117 _interference_graph[live_operand].emplace_back(ind);
118 }
119 _live_operands.emplace(ind);
120
121 VERBOSE(WIC_PLANNER) << "claim(" << ind << "): [" << size << "sz]" << std::endl;
122}
123
125{
126 _live_operands.erase(ind);
127 VERBOSE(WIC_PLANNER) << "release(" << ind << ")" << std::endl;
128}
129
130/*
131 * Build memory plans using liveness and size of operands
132 * 1. Build inference graph at claim
133 * - Two operands interfere if they have overlapped live range
134 * 2. Sort operands in descending order of size
135 * - Use std::multimap to sort operands
136 * 3. Allocate memory block for sorted operands
137 * - Find free memory block which does not overlap with interfered operands
138 */
139void WICPlanner::buildMemoryPlans()
140{
141 for (const auto &[size, ind] : _operands)
142 {
143 VERBOSE(WIC_PLANNER) << "build_plan(" << ind << "): [" << size << "sz]" << std::endl;
144
145 uint32_t next_offset = 0;
146 if (_interference_graph.count(ind))
147 {
148 // Find interfered memory plans and sort them by offset
149 std::multimap<uint32_t, uint32_t> interfered_plans;
150 for (const auto &interference : _interference_graph[ind])
151 {
152 if (_mem_plans.count(interference))
153 interfered_plans.emplace(_mem_plans[interference].offset, _mem_plans[interference].size);
154 }
155
156 // Find free memory block in first-fit manner
157 for (const auto &[claimed_base_offset, claimed_size] : interfered_plans)
158 {
159 VERBOSE(WIC_PLANNER) << "interfere : [+" << claimed_base_offset << ", " << claimed_size
160 << "sz]" << std::endl;
161 if (next_offset + size <= claimed_base_offset)
162 {
163 break;
164 }
165 else if (next_offset < claimed_base_offset + claimed_size)
166 {
167 next_offset = claimed_base_offset + claimed_size;
168 }
169 }
170 }
171 else
172 {
173 VERBOSE(WIC_PLANNER) << "No interference" << std::endl;
174 }
175
176 _mem_plans[ind] = {next_offset, size};
177 VERBOSE(WIC_PLANNER) << "alloc(" << ind << "): [+" << next_offset << ", " << size << "sz]"
178 << std::endl;
179
180 if (_capacity < next_offset + size)
181 {
182 _capacity = next_offset + size;
183 }
184 }
185 _initialized = true;
186 _interference_graph.clear();
187 _operands.clear();
188}
189
191{
192 if (!_initialized)
193 buildMemoryPlans();
194 return _mem_plans;
195}
196
197} // namespace onert::backend::basic
void release(const ir::OperandIndex &) override
Release memory for operand by bump way.
void claim(const ir::OperandIndex &, size_t) override
Claim memory for operand by bump way.
void claim(const ir::OperandIndex &, size_t) override
Claim memory for operand by firstfit way.
void release(const ir::OperandIndex &) override
Release memory for operand by firstfit way.
void claim(const ir::OperandIndex &, size_t) override
Claim memory for operand by WIC algorithm.
MemoryPlans & memory_plans() override
Get MemoryPlans.
void release(const ir::OperandIndex &) override
Release memory for operand by WIC algorithm.
T value() const
Return underlying value.
Definition Index.h:137
__global uchar * offset(const Image *img, int x, int y)
Definition helpers.h:540
#define VERBOSE(name, lv)
Definition Log.h:71
int32_t size[5]
Definition Slice.cpp:35
Structure to have memory offset and size.
std::unordered_map< ir::OperandIndex, Block > MemoryPlans