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) 2024 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
21
22#include <util/logging.h>
23
24#include <cassert>
25
27{
28
29template <typename Index> void BumpPlanner<Index>::claim(const Index &ind, size_t size)
30{
31 basic::Block blk{_capacity, size};
32 _mem_plans[ind] = blk;
33 _capacity += size;
34
35 VERBOSE(BP_PLANNER) << "CLAIM(" << ind << "): " << blk.offset << ", " << blk.size << std::endl;
36}
37
38template <typename Index> void BumpPlanner<Index>::release(const Index &ind)
39{
40 VERBOSE(BP_PLANNER) << "RELEASE(" << ind << "): "
41 << "NOTHING does" << std::endl;
42}
43
44// There are some assumptions for claiming memory(== making a reservation for memory).
45// 1. About _claim_table(std::map).
46// - The table's data structure is std::map so that it always sorts
47// value(Index) by key(base_offset).
48// - This claim() inserts key/value into _claim_table and the release() removes the key/value from
49// _claim_table.
50// - _claim_table shows the memory status at a certain point in time. Therefore,
51// - If _claim_table has an offset and a certain size at a certain point in time,
52// it means the place at the offset has been already claimed(== can't claim now. need to find
53// someplace new).
54// - If _claim_table doesn't have any element for an offset and a certain size at a certain
55// point in time, it means the place at the offset can be claimed.
56// 2. In the loop for _claim_table, we can assume the current claim_base_offset value is bigger than
57// the previous claim_base_offset.
58template <typename Index> void FirstFitPlanner<Index>::claim(const Index &ind, size_t size)
59{
60 // Find the right position for claiming
61 uint32_t next_offset = 0;
62 for (const auto &[claimed_base_offset, claimed_index] : _claim_table)
63 {
64 auto claimed_size = _mem_plans[claimed_index].size;
65 if (next_offset + size <= claimed_base_offset)
66 {
67 break;
68 }
69 else
70 {
71 next_offset = claimed_base_offset + claimed_size;
72 }
73 }
74
75 // Now next_offset is set to the proper offset
76 // std::map's operator[] requires default constructor of value
77 // _claim_table[next_offset] = ind;
78 _claim_table.emplace(std::make_pair(next_offset, ind));
79 _mem_plans[ind] = {next_offset, size};
80
81 VERBOSE(FF_PLANNER) << "claim(" << ind << "): [+" << next_offset << ", " << size << "sz]"
82 << std::endl;
83
84 if (_capacity < next_offset + size)
85 {
86 _capacity = next_offset + size;
87 }
88}
89
90template <typename Index> void FirstFitPlanner<Index>::release(const Index &ind)
91{
92 for (auto it = _claim_table.cbegin(); it != _claim_table.cend(); ++it)
93 {
94 if (it->second == ind)
95 {
96 uint32_t offset = it->first;
97 uint32_t size = _mem_plans[ind].size;
98
99 _claim_table.erase(it);
100
101 VERBOSE(FF_PLANNER) << "release(" << ind << "): [+" << offset << ", " << size << "sz]"
102 << std::endl;
103 return;
104 }
105 }
106 assert(!"Cannot release for given index. It has been not claimed or released already.");
107}
108
109template <typename Index>
111 : _initialized(false), _capacity(0), _mem_plans(), _live_indices(), _interference_graph(),
112 _indices()
113{
114 // DO NOTHING
115}
116
117template <typename Index> void WICPlanner<Index>::claim(const Index &ind, size_t size)
118{
119 _indices.emplace(size, ind);
120 _interference_graph[ind].insert(_interference_graph[ind].end(), _live_indices.cbegin(),
121 _live_indices.cend());
122 for (const auto &live_operand : _live_indices)
123 {
124 _interference_graph[live_operand].emplace_back(ind);
125 }
126 _live_indices.emplace(ind);
127
128 VERBOSE(WIC_PLANNER) << "claim(" << ind << "): [" << size << "sz]" << std::endl;
129}
130
131template <typename Index> void WICPlanner<Index>::release(const Index &ind)
132{
133 _live_indices.erase(ind);
134 VERBOSE(WIC_PLANNER) << "release(" << ind << ")" << std::endl;
135}
136
137/*
138 * Build memory plans using liveness and size of operands
139 * 1. Build inference graph at claim
140 * - Two operands interfere if they have overlapped live range
141 * 2. Sort operands in descending order of size
142 * - Use std::multimap to sort operands
143 * 3. Allocate memory block for sorted operands
144 * - Find free memory block which does not overlap with interfered operands
145 */
146
147template <typename Index> void WICPlanner<Index>::buildMemoryPlans()
148{
149 for (const auto &[size, ind] : _indices)
150 {
151 VERBOSE(WIC_PLANNER) << "build_plan(" << ind << "): [" << size << "sz]" << std::endl;
152
153 uint32_t next_offset = 0;
154 if (_interference_graph.count(ind))
155 {
156 // Find interfered memory plans and sort them by offset
157 std::multimap<uint32_t, uint32_t> interfered_plans;
158 for (const auto &interference : _interference_graph[ind])
159 {
160 if (_mem_plans.count(interference))
161 interfered_plans.emplace(_mem_plans[interference].offset, _mem_plans[interference].size);
162 }
163
164 // Find free memory block in first-fit manner
165 for (const auto &[claimed_base_offset, claimed_size] : interfered_plans)
166 {
167 VERBOSE(WIC_PLANNER) << "interfere : [+" << claimed_base_offset << ", " << claimed_size
168 << "sz]" << std::endl;
169 if (next_offset + size <= claimed_base_offset)
170 {
171 break;
172 }
173 else if (next_offset < claimed_base_offset + claimed_size)
174 {
175 next_offset = claimed_base_offset + claimed_size;
176 }
177 }
178 }
179 else
180 {
181 VERBOSE(WIC_PLANNER) << "No interference" << std::endl;
182 }
183
184 _mem_plans[ind] = {next_offset, size};
185 VERBOSE(WIC_PLANNER) << "alloc(" << ind << "): [+" << next_offset << ", " << size << "sz]"
186 << std::endl;
187
188 if (_capacity < next_offset + size)
189 {
190 _capacity = next_offset + size;
191 }
192 }
193 _initialized = true;
194 _interference_graph.clear();
195 _indices.clear();
196}
197
198template <typename Index> typename WICPlanner<Index>::MemoryPlans &WICPlanner<Index>::memory_plans()
199{
200 if (!_initialized)
201 buildMemoryPlans();
202 return _mem_plans;
203}
204
207
210
213
214} // namespace onert::backend::train
Class to plan memory by bump way.
void release(const Index &) override
Release memory for tensor by bump way.
void claim(const Index &, size_t) override
Claim memory for tensor by bump way.
Class to plan memory by firstfit way.
void claim(const Index &, size_t) override
Claim memory for tensor by firstfit way.
void release(const Index &) override
Release memory for tensor by firstfit way.
Class to plan memory by Weighted Interval Color algorithm.
void release(const Index &) override
Release memory for tensor by WIC algorithm.
MemoryPlans & memory_plans() override
Get MemoryPlans.
void claim(const Index &, size_t) override
Claim memory for tensor by WIC algorithm.
__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.