2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__
18 #define __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__
23 #include <arm_compute/core/Types.h>
24 #include <backend/ITensorBuilder.h>
25 #include "ir/OperandIndexMap.h"
26 #include <ir/Operands.h>
27 #include "AclTensorManager.h"
28 #include "AclTensorRegistry.h"
30 #include "ParentInfo.h"
31 #include <util/Utils.h>
46 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
47 class AclTensorBuilder : public ITensorBuilder
50 using T_AclTensorManager = AclTensorManager<T_ITensor, T_Tensor, T_SubTensor>;
52 AclTensorBuilder(const ir::Operands &operands, T_AclTensorManager *tensor_mgr,
53 const std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> &tensor_reg);
56 * @brief Register tensor information to allocate on ACL-CL backend
57 * @param[in] ind Operand index
58 * @param[in] info Tensor information
59 * @param[in] layout Tensor data layout
61 void registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info,
62 ir::Layout backend_layout) override;
64 void notifyFirstUse(const ir::OperandIndex &) override;
65 void notifyLastUse(const ir::OperandIndex &) override;
67 bool isRegistered(const ir::OperandIndex &) const override;
69 void prepare(void) override;
70 void allocate() override;
71 void postFunctionPrepare() override;
73 std::unique_ptr<ITensorManager> releaseStaticTensorManager(void) override;
75 T_AclTensorManager *acl_tensor_manager(void) { return _tensor_mgr.get(); }
77 void setUsesCount(const ir::OperandIndex &index, size_t num_uses)
79 assert(_uses_count_map.find(index) != _uses_count_map.end() ? _uses_count_map[index] == num_uses
81 _uses_count_map[index] = num_uses;
84 void parent_map(std::unordered_map<ir::OperandIndex, ParentInfo> &&parent_map)
86 _parent_map = std::move(parent_map);
89 bool areSubTensorsOf(const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq);
92 * @brief Check child tensor is allocated as subtensor of parent tensor
93 * @param[in] parent Index of parent
94 * @param[in] child Index of child
95 * @return @c true if child is allocated as subtensor of parent, otherwise @c false
97 bool isSubTensorOf(const ir::OperandIndex &parent, const ir::OperandIndex &child);
100 void buildTensors(void);
101 ir::OperandIndex findRootParent(ir::OperandIndex index);
104 const ir::Operands &_operands;
105 ir::OperandIndexMap<ir::OperandInfo> _tensor_info_map;
106 ir::OperandIndexMap<ir::Layout> _tensor_layout_map;
107 ir::OperandIndexMap<size_t> _uses_count_map;
109 std::unique_ptr<T_AclTensorManager> _tensor_mgr;
110 std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> _tensor_reg;
112 // for linear executor
113 std::vector<std::pair<UsesType, ir::OperandIndex>> _lifetime_seq;
115 // Extra info for concat elimination
116 ir::OperandIndexMap<ParentInfo> _parent_map;
119 } // namespace acl_common
120 } // namespace backend
128 #include "util/logging.h"
137 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
138 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::AclTensorBuilder(
139 const ir::Operands &operands, T_AclTensorManager *tensor_mgr,
140 const std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> &tensor_reg)
141 : _operands{operands}, _tensor_mgr{tensor_mgr}, _tensor_reg{tensor_reg}
146 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
147 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::registerTensorInfo(
148 const ir::OperandIndex &ind, const ir::OperandInfo &info, ir::Layout backend_layout)
150 assert(_tensor_mgr->constTensors().size() == 0);
151 assert(_tensor_mgr->nonconstTensors().size() == 0);
153 _uses_count_map[ind] = _operands.at(ind).getUses().size();
155 if (_parent_map.count(ind) == 0)
158 _tensor_info_map.emplace(ind, info);
159 _tensor_layout_map.insert({ind, backend_layout});
165 assert(!info.isConstant() && "Subtensors of constants are not supported yet.");
167 // Update offset info and emplace
168 auto &parent_info = _parent_map[ind];
169 const auto &obj = _operands.at(ind);
170 auto parent_index = parent_info.parent;
171 auto &offset = parent_info.coordinates;
172 auto frontend_layout = parent_info.frontend_layout;
174 assert(obj.shape().rank() <= ir::Shape::MAX_RANK);
175 auto shape = obj.shape();
176 if (_operands.at(parent_index).shape().rank() >= 4 && frontend_layout == ir::Layout::NHWC &&
177 backend_layout == ir::Layout::NCHW)
179 // Permutation changing layout beyond 4-D is not supported yet
180 const auto parent_rank = _operands.at(parent_index).shape().rank();
181 assert(parent_rank == 4);
182 shape.extendRank(parent_rank);
183 offset = {offset[0], offset[3], offset[1], offset[2]};
185 else if (_operands.at(parent_index).shape().rank() >= 4 &&
186 frontend_layout == ir::Layout::NHWC && backend_layout == ir::Layout::NCHW)
188 // Permutation changing layout beyond 4-D is not supported yet
189 const auto parent_rank = _operands.at(parent_index).shape().rank();
190 assert(parent_rank == 4);
191 shape.extendRank(parent_rank);
192 offset = {offset[0], offset[2], offset[3], offset[1]};
194 auto new_shape = permuteShape(shape, frontend_layout, backend_layout);
195 auto oi = ir::OperandInfo::createStaticInfo(new_shape, obj.typeInfo());
196 _tensor_info_map.emplace(ind, oi);
200 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
201 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyFirstUse(const ir::OperandIndex &ind)
203 _lifetime_seq.emplace_back(UsesType::FIRST, ind);
206 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
207 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyLastUse(const ir::OperandIndex &ind)
209 _lifetime_seq.emplace_back(UsesType::LAST, ind);
212 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
213 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isRegistered(
214 const ir::OperandIndex &ind) const
216 return _tensor_info_map.find(ind) != _tensor_info_map.end();
219 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
220 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::prepare(void)
225 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
226 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::allocate(void)
228 // Update lifetime sequence to apply subtensor optimization
230 std::unordered_map<ir::OperandIndex, ir::OperandIndex> root_map;
231 std::function<ir::OperandIndex &(ir::OperandIndex)> find_root =
232 [&](ir::OperandIndex ind) -> ir::OperandIndex & {
233 ir::OperandIndex &ret = root_map[ind];
235 // We know the root parent value already
239 auto itr = _parent_map.find(ind);
240 if (itr == _parent_map.end())
242 // If there is no parent, let's store the value of itself
247 return ret = find_root(itr->second.parent);
251 ir::OperandIndexMap<bool> first_use_check;
252 ir::OperandIndexMap<bool> last_use_check;
253 std::map<size_t, std::pair<UsesType, ir::OperandIndex>> lifetime_map;
254 for (size_t i = 0; i < _lifetime_seq.size(); i++)
256 auto &entry = _lifetime_seq[i];
257 if (entry.first != UsesType::FIRST)
259 auto root_ind = find_root(entry.second);
260 if (first_use_check[root_ind])
262 first_use_check[root_ind] = true;
263 lifetime_map[i] = {UsesType::FIRST, root_ind};
266 for (int i = _lifetime_seq.size() - 1; i >= 0; i--)
268 auto &entry = _lifetime_seq[i];
269 if (entry.first != UsesType::LAST)
271 auto root_ind = find_root(entry.second);
272 if (last_use_check[root_ind])
274 last_use_check[root_ind] = true;
275 lifetime_map[i] = {UsesType::LAST, root_ind};
278 for (auto &entry : lifetime_map)
280 auto &use = entry.second;
281 auto use_type = use.first;
282 auto use_index = use.second;
283 assert(use_index.valid());
284 if (use_type == UsesType::FIRST)
285 _tensor_mgr->startLifetime(use_index);
287 _tensor_mgr->finishLifetime(use_index);
290 _tensor_mgr->allocateConsts();
292 // TODO Since `_parent_map` is filled for all Concat nodes even if the node this backend uses
293 // After refactoring BackendContext we can uncomment this
294 // assert(_tensor_info_map.size() ==
295 // _tensor_mgr->nonconstTensors().size() + num of constants of _tensor_info_map +
296 // _parent_map.size());
297 _tensor_mgr->allocateNonconsts();
299 _tensor_mgr->allocateInternalBufferManager();
302 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
303 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::postFunctionPrepare(void)
305 _tensor_mgr->tryDeallocConstants();
308 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
309 std::unique_ptr<ITensorManager>
310 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::releaseStaticTensorManager(void)
312 return std::move(_tensor_mgr);
315 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
316 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::buildTensors(void)
318 assert(_tensor_mgr->constTensors().size() == 0);
319 assert(_tensor_mgr->nonconstTensors().size() == 0);
322 for (auto &entry : _tensor_info_map)
324 auto ind = entry.first;
325 if (_parent_map.count(ind) > 0)
328 const auto &info = entry.second;
329 const auto &backend_layout = _tensor_layout_map[ind];
331 asTensorInfo(info.shape(), info.typeInfo(), ir::Layout::UNKNOWN, backend_layout, true);
332 _tensor_mgr->buildTensor(ind, tensor_info, info.shape().rank(), info.isConstant(),
333 _uses_count_map[ind]);
337 assert(_tensor_mgr->nonconstSubtensors().size() == 0);
338 // TODO Iterate `_parent_map` instead, once the optimizer bug is fixed
339 // `Optimizer` iterates the entire OpSequences, so there is a bug if iterating _parent_map
340 for (auto &entry : _tensor_info_map)
342 auto ind = entry.first;
343 if (_parent_map.count(ind) == 0)
346 // To make subtensor, parent tensor must be made first
347 // For this condition, use stack
348 // 1) Push one subtensor index to stack (iterate subtensors)
349 // 2) If tensor at stack top is already made, pop and go to 4)
350 // 3) If tensor pushed at 1) is not made, check parent tensor
351 // 3-1) If parent tensor is already made, we can make child tensor
352 // Make child tensor and pop, go to 4)
353 // 3-2) If parent tensor is not made, we can't make child tensor yet
354 // Push parent tensor index to stack and return to 4)
355 // 4) If stack is empty, return to 1), else return to 2)
356 auto &subtensors = _tensor_mgr->nonconstSubtensors();
358 std::stack<ir::OperandIndex> stack;
361 while (!stack.empty())
363 const auto current = stack.top();
364 const auto &tensor_info = _tensor_info_map.at(current);
365 const auto &parent_info = _parent_map.at(current);
367 // Already generated SubTensor
368 if (subtensors.find(current) != subtensors.end())
374 auto parent = parent_info.parent;
375 std::shared_ptr<T_ITensor> parent_tensor = _tensor_mgr->findTensorAsParent(parent);
378 // Cannot find allocated parent tensor: allocate parent first
379 assert(_parent_map.count(parent) > 0);
383 assert(parent_tensor != nullptr);
385 // Child's type should be same with parent
386 assert(tensor_info.typeInfo().offset() ==
387 parent_tensor->info()->quantization_info().uniform().offset);
388 assert(tensor_info.typeInfo().scale() ==
389 parent_tensor->info()->quantization_info().uniform().scale);
390 assert(tensor_info.typeInfo().type() == parent_tensor->data_type());
392 // NOTE SubTensor's layout must be the same with layout of parent tensor
393 const auto &root_parent = findRootParent(parent);
394 const auto &backend_layout = _tensor_layout_map[root_parent];
396 auto shape = asTensorShape(tensor_info.shape(), ir::Layout::UNKNOWN, backend_layout, true);
397 ::arm_compute::Coordinates coordinates =
398 asTensorCoordinate(parent_info.coordinates, ir::Layout::UNKNOWN, backend_layout);
399 _tensor_mgr->buildSubtensor(parent, current, shape, coordinates, tensor_info.shape().rank(),
406 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
407 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::areSubTensorsOf(
408 const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq)
410 for (auto &cand : seq)
412 if (!isSubTensorOf(parent, cand))
420 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
421 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isSubTensorOf(
422 const ir::OperandIndex &parent, const ir::OperandIndex &child)
424 auto itr = _parent_map.find(child);
425 if (itr == _parent_map.end())
430 return itr->second.parent == parent;
433 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
435 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::findRootParent(ir::OperandIndex ind)
437 if (_parent_map.find(ind) == _parent_map.end())
440 auto parent_ind = _parent_map.at(ind).parent;
441 return findRootParent(parent_ind);
444 } // namespace acl_common
445 } // namespace backend
448 #endif // __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__