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"
29 #include "ParentInfo.h"
30 #include <util/Utils.h>
45 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
46 class AclTensorBuilder : public ITensorBuilder
49 using T_AclTensorManager = AclTensorManager<T_ITensor, T_Tensor, T_SubTensor>;
51 AclTensorBuilder(const ir::Operands &operands, T_AclTensorManager *tensor_mgr);
54 * @brief Register tensor information to allocate on ACL-CL backend
55 * @param[in] ind Operand index
56 * @param[in] info Tensor information
57 * @param[in] layout Tensor data layout
59 void registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info,
60 ir::Layout backend_layout) override;
62 void notifyFirstUse(const ir::OperandIndex &) override;
63 void notifyLastUse(const ir::OperandIndex &) override;
65 bool isRegistered(const ir::OperandIndex &) const override;
66 std::shared_ptr<backend::ITensorRegistry> tensorRegistry() override { return nullptr; }
68 void prepare(void) override;
69 void allocate() override;
70 void postFunctionPrepare() override;
72 std::shared_ptr<ITensor> tensorAt(const ir::OperandIndex &ind) override;
73 void iterate(const IterateFunction &fn) override;
75 std::unique_ptr<ITensorManager> releaseStaticTensorManager(void) override;
77 std::shared_ptr<T_ITensor> at(const ir::OperandIndex &ind);
79 T_AclTensorManager *acl_tensor_manager(void) { return _tensor_mgr.get(); }
81 void setUsesCount(const ir::OperandIndex &index, size_t num_uses)
83 assert(_uses_count_map.find(index) != _uses_count_map.end() ? _uses_count_map[index] == num_uses
85 _uses_count_map[index] = num_uses;
88 void parent_map(std::unordered_map<ir::OperandIndex, ParentInfo> &&parent_map)
90 _parent_map = std::move(parent_map);
93 bool areSubTensorsOf(const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq);
96 * @brief Check child tensor is allocated as subtensor of parent tensor
97 * @param[in] parent Index of parent
98 * @param[in] child Index of child
99 * @return @c true if child is allocated as subtensor of parent, otherwise @c false
101 bool isSubTensorOf(const ir::OperandIndex &parent, const ir::OperandIndex &child);
103 bool supportDynamicTensor() override { return false; }
106 void buildTensors(void);
107 ir::OperandIndex findRootParent(ir::OperandIndex index);
110 const ir::Operands &_operands;
111 ir::OperandIndexMap<ir::OperandInfo> _tensor_info_map;
112 ir::OperandIndexMap<ir::Layout> _tensor_layout_map;
113 ir::OperandIndexMap<size_t> _uses_count_map;
115 std::unique_ptr<T_AclTensorManager> _tensor_mgr;
117 // for linear executor
118 std::vector<std::pair<UsesType, ir::OperandIndex>> _lifetime_seq;
120 // Extra info for concat elimination
121 ir::OperandIndexMap<ParentInfo> _parent_map;
124 } // namespace acl_common
125 } // namespace backend
133 #include "util/logging.h"
142 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
143 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::AclTensorBuilder(const ir::Operands &operands,
144 T_AclTensorManager *tensor_mgr)
145 : _operands{operands}, _tensor_mgr{tensor_mgr}
150 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
151 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::registerTensorInfo(
152 const ir::OperandIndex &ind, const ir::OperandInfo &info, ir::Layout backend_layout)
154 assert(_tensor_mgr->constTensors().size() == 0);
155 assert(_tensor_mgr->nonconstTensors().size() == 0);
157 _uses_count_map[ind] = _operands.at(ind).getUses().size();
159 if (_parent_map.count(ind) == 0)
162 _tensor_info_map.emplace(ind, info);
163 _tensor_layout_map.insert({ind, backend_layout});
169 assert(!info.isConstant() && "Subtensors of constants are not supported yet.");
171 // Update offset info and emplace
172 auto &parent_info = _parent_map[ind];
173 const auto &obj = _operands.at(ind);
174 auto parent_index = parent_info.parent;
175 auto &offset = parent_info.coordinates;
176 auto frontend_layout = parent_info.frontend_layout;
178 assert(obj.shape().rank() <= ir::Shape::MAX_RANK);
179 auto shape = obj.shape();
180 if (_operands.at(parent_index).shape().rank() >= 4 && frontend_layout == ir::Layout::NHWC &&
181 backend_layout == ir::Layout::NCHW)
183 // Permutation changing layout beyond 4-D is not supported yet
184 const auto parent_rank = _operands.at(parent_index).shape().rank();
185 assert(parent_rank == 4);
186 shape.extendRank(parent_rank);
187 offset = {offset[0], offset[3], offset[1], offset[2]};
189 else if (_operands.at(parent_index).shape().rank() >= 4 &&
190 frontend_layout == ir::Layout::NHWC && backend_layout == ir::Layout::NCHW)
192 // Permutation changing layout beyond 4-D is not supported yet
193 const auto parent_rank = _operands.at(parent_index).shape().rank();
194 assert(parent_rank == 4);
195 shape.extendRank(parent_rank);
196 offset = {offset[0], offset[2], offset[3], offset[1]};
198 auto new_shape = permuteShape(shape, frontend_layout, backend_layout);
199 auto oi = ir::OperandInfo::createStaticInfo(new_shape, obj.typeInfo());
200 _tensor_info_map.emplace(ind, oi);
204 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
205 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyFirstUse(const ir::OperandIndex &ind)
207 _lifetime_seq.emplace_back(UsesType::FIRST, ind);
210 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
211 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyLastUse(const ir::OperandIndex &ind)
213 _lifetime_seq.emplace_back(UsesType::LAST, ind);
216 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
217 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isRegistered(
218 const ir::OperandIndex &ind) const
220 return _tensor_info_map.find(ind) != _tensor_info_map.end();
223 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
224 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::prepare(void)
229 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
230 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::allocate(void)
232 // Update lifetime sequence to apply subtensor optimization
234 std::unordered_map<ir::OperandIndex, ir::OperandIndex> root_map;
235 std::function<ir::OperandIndex &(ir::OperandIndex)> find_root =
236 [&](ir::OperandIndex ind) -> ir::OperandIndex & {
237 ir::OperandIndex &ret = root_map[ind];
239 // We know the root parent value already
243 auto itr = _parent_map.find(ind);
244 if (itr == _parent_map.end())
246 // If there is no parent, let's store the value of itself
251 return ret = find_root(itr->second.parent);
255 ir::OperandIndexMap<bool> first_use_check;
256 ir::OperandIndexMap<bool> last_use_check;
257 std::map<size_t, std::pair<UsesType, ir::OperandIndex>> lifetime_map;
258 for (size_t i = 0; i < _lifetime_seq.size(); i++)
260 auto &entry = _lifetime_seq[i];
261 if (entry.first != UsesType::FIRST)
263 auto root_ind = find_root(entry.second);
264 if (first_use_check[root_ind])
266 first_use_check[root_ind] = true;
267 lifetime_map[i] = {UsesType::FIRST, root_ind};
270 for (int i = _lifetime_seq.size() - 1; i >= 0; i--)
272 auto &entry = _lifetime_seq[i];
273 if (entry.first != UsesType::LAST)
275 auto root_ind = find_root(entry.second);
276 if (last_use_check[root_ind])
278 last_use_check[root_ind] = true;
279 lifetime_map[i] = {UsesType::LAST, root_ind};
282 for (auto &entry : lifetime_map)
284 auto &use = entry.second;
285 auto use_type = use.first;
286 auto use_index = use.second;
287 assert(use_index.valid());
288 if (use_type == UsesType::FIRST)
289 _tensor_mgr->startLifetime(use_index);
291 _tensor_mgr->finishLifetime(use_index);
294 _tensor_mgr->allocateConsts();
296 // TODO Since `_parent_map` is filled for all Concat nodes even if the node this backend uses
297 // After refactoring BackendContext we can uncomment this
298 // assert(_tensor_info_map.size() ==
299 // _tensor_mgr->nonconstTensors().size() + num of constants of _tensor_info_map +
300 // _parent_map.size());
301 _tensor_mgr->allocateNonconsts();
303 _tensor_mgr->allocateInternalBufferManager();
306 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
307 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::postFunctionPrepare(void)
309 _tensor_mgr->tryDeallocConstants();
312 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
313 std::shared_ptr<ITensor>
314 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::tensorAt(const ir::OperandIndex &ind)
316 return _tensor_mgr->at(ind);
319 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
320 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::iterate(const IterateFunction &fn)
322 _tensor_mgr->iterate(fn);
325 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
326 std::shared_ptr<T_ITensor>
327 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::at(const ir::OperandIndex &ind)
329 auto ret = _tensor_mgr->at(ind);
330 assert(ret != nullptr);
334 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
335 std::unique_ptr<ITensorManager>
336 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::releaseStaticTensorManager(void)
338 return std::move(_tensor_mgr);
341 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
342 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::buildTensors(void)
344 assert(_tensor_mgr->constTensors().size() == 0);
345 assert(_tensor_mgr->nonconstTensors().size() == 0);
348 for (auto &entry : _tensor_info_map)
350 auto ind = entry.first;
351 if (_parent_map.count(ind) > 0)
354 const auto &info = entry.second;
355 const auto &backend_layout = _tensor_layout_map[ind];
357 asTensorInfo(info.shape(), info.typeInfo(), ir::Layout::UNKNOWN, backend_layout, true);
358 _tensor_mgr->buildTensor(ind, tensor_info, info.shape().rank(), info.isConstant(),
359 _uses_count_map[ind]);
363 assert(_tensor_mgr->nonconstSubtensors().size() == 0);
364 // TODO Iterate `_parent_map` instead, once the optimizer bug is fixed
365 // `Optimizer` iterates the entire OpSequences, so there is a bug if iterating _parent_map
366 for (auto &entry : _tensor_info_map)
368 auto ind = entry.first;
369 if (_parent_map.count(ind) == 0)
372 // To make subtensor, parent tensor must be made first
373 // For this condition, use stack
374 // 1) Push one subtensor index to stack (iterate subtensors)
375 // 2) If tensor at stack top is already made, pop and go to 4)
376 // 3) If tensor pushed at 1) is not made, check parent tensor
377 // 3-1) If parent tensor is already made, we can make child tensor
378 // Make child tensor and pop, go to 4)
379 // 3-2) If parent tensor is not made, we can't make child tensor yet
380 // Push parent tensor index to stack and return to 4)
381 // 4) If stack is empty, return to 1), else return to 2)
382 auto &subtensors = _tensor_mgr->nonconstSubtensors();
384 std::stack<ir::OperandIndex> stack;
387 while (!stack.empty())
389 const auto current = stack.top();
390 const auto &tensor_info = _tensor_info_map.at(current);
391 const auto &parent_info = _parent_map.at(current);
393 // Already generated SubTensor
394 if (subtensors.find(current) != subtensors.end())
400 auto parent = parent_info.parent;
401 std::shared_ptr<T_ITensor> parent_tensor = _tensor_mgr->findTensorAsParent(parent);
404 // Cannot find allocated parent tensor: allocate parent first
405 assert(_parent_map.count(parent) > 0);
409 assert(parent_tensor != nullptr);
411 // Child's type should be same with parent
412 assert(tensor_info.typeInfo().offset() ==
413 parent_tensor->info()->quantization_info().uniform().offset);
414 assert(tensor_info.typeInfo().scale() ==
415 parent_tensor->info()->quantization_info().uniform().scale);
416 assert(tensor_info.typeInfo().type() == parent_tensor->data_type());
418 // NOTE SubTensor's layout must be the same with layout of parent tensor
419 const auto &root_parent = findRootParent(parent);
420 const auto &backend_layout = _tensor_layout_map[root_parent];
422 auto shape = asTensorShape(tensor_info.shape(), ir::Layout::UNKNOWN, backend_layout, true);
423 ::arm_compute::Coordinates coordinates =
424 asTensorCoordinate(parent_info.coordinates, ir::Layout::UNKNOWN, backend_layout);
425 _tensor_mgr->buildSubtensor(parent, current, shape, coordinates, tensor_info.shape().rank(),
432 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
433 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::areSubTensorsOf(
434 const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq)
436 for (auto &cand : seq)
438 if (!isSubTensorOf(parent, cand))
446 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
447 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isSubTensorOf(
448 const ir::OperandIndex &parent, const ir::OperandIndex &child)
450 auto itr = _parent_map.find(child);
451 if (itr == _parent_map.end())
456 return itr->second.parent == parent;
459 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
461 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::findRootParent(ir::OperandIndex ind)
463 if (_parent_map.find(ind) == _parent_map.end())
466 auto parent_ind = _parent_map.at(ind).parent;
467 return findRootParent(parent_ind);
470 } // namespace acl_common
471 } // namespace backend
474 #endif // __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__