bb7abc95d66928534df23bc1bcb0b3f8e9b9b663
[platform/core/ml/nnfw.git] / runtime / onert / backend / acl_common / AclTensorBuilder.h
1 /*
2  * Copyright (c) 2019 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 #ifndef __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__
18 #define __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__
19
20 #include <memory>
21 #include <queue>
22
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"
29 #include <memory>
30 #include "ParentInfo.h"
31 #include <util/Utils.h>
32
33 namespace onert
34 {
35 namespace backend
36 {
37 namespace acl_common
38 {
39
40 enum class UsesType
41 {
42   FIRST,
43   LAST
44 };
45
46 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
47 class AclTensorBuilder : public ITensorBuilder
48 {
49 public:
50   using T_AclTensorManager = AclTensorManager<T_ITensor, T_Tensor, T_SubTensor>;
51
52   AclTensorBuilder(const ir::Operands &operands, T_AclTensorManager *tensor_mgr,
53                    const std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> &tensor_reg);
54
55   /**
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
60    */
61   void registerTensorInfo(const ir::OperandIndex &ind, const ir::OperandInfo &info,
62                           ir::Layout backend_layout) override;
63
64   void notifyFirstUse(const ir::OperandIndex &) override;
65   void notifyLastUse(const ir::OperandIndex &) override;
66
67   bool isRegistered(const ir::OperandIndex &) const override;
68
69   void prepare(void) override;
70   void allocate() override;
71   void postFunctionPrepare() override;
72
73   T_AclTensorManager *acl_tensor_manager(void) { return _tensor_mgr.get(); }
74
75   void setUsesCount(const ir::OperandIndex &index, size_t num_uses)
76   {
77     assert(_uses_count_map.find(index) != _uses_count_map.end() ? _uses_count_map[index] == num_uses
78                                                                 : true);
79     _uses_count_map[index] = num_uses;
80   }
81
82   void parent_map(std::unordered_map<ir::OperandIndex, ParentInfo> &&parent_map)
83   {
84     _parent_map = std::move(parent_map);
85   }
86
87   bool areSubTensorsOf(const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq);
88
89   /**
90    * @brief     Check child tensor is allocated as subtensor of parent tensor
91    * @param[in] parent  Index of parent
92    * @param[in] child   Index of child
93    * @return    @c true if child is allocated as subtensor of parent, otherwise @c false
94    */
95   bool isSubTensorOf(const ir::OperandIndex &parent, const ir::OperandIndex &child);
96
97 private:
98   void buildTensors(void);
99   ir::OperandIndex findRootParent(ir::OperandIndex index);
100
101 private:
102   const ir::Operands &_operands;
103   ir::OperandIndexMap<ir::OperandInfo> _tensor_info_map;
104   ir::OperandIndexMap<ir::Layout> _tensor_layout_map;
105   ir::OperandIndexMap<size_t> _uses_count_map;
106
107   std::unique_ptr<T_AclTensorManager> _tensor_mgr;
108   std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> _tensor_reg;
109
110   // for linear executor
111   std::vector<std::pair<UsesType, ir::OperandIndex>> _lifetime_seq;
112
113   // Extra info for concat elimination
114   ir::OperandIndexMap<ParentInfo> _parent_map;
115 };
116
117 } // namespace acl_common
118 } // namespace backend
119 } // namespace onert
120
121 #include <cassert>
122 #include <stack>
123
124 #include "Convert.h"
125
126 #include "util/logging.h"
127
128 namespace onert
129 {
130 namespace backend
131 {
132 namespace acl_common
133 {
134
135 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
136 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::AclTensorBuilder(
137     const ir::Operands &operands, T_AclTensorManager *tensor_mgr,
138     const std::shared_ptr<AclTensorRegistry<T_AclTensorManager>> &tensor_reg)
139     : _operands{operands}, _tensor_mgr{tensor_mgr}, _tensor_reg{tensor_reg}
140 {
141   assert(_tensor_mgr);
142 }
143
144 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
145 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::registerTensorInfo(
146     const ir::OperandIndex &ind, const ir::OperandInfo &info, ir::Layout backend_layout)
147 {
148   assert(_tensor_mgr->constTensors().size() == 0);
149   assert(_tensor_mgr->nonconstTensors().size() == 0);
150
151   _uses_count_map[ind] = _operands.at(ind).getUses().size();
152
153   if (_parent_map.count(ind) == 0)
154   {
155     // Normal Tensors
156     _tensor_info_map.emplace(ind, info);
157     _tensor_layout_map.insert({ind, backend_layout});
158   }
159   else
160   {
161     // SubTensors
162     assert(!info.isConstant() && "Subtensors of constants are not supported yet.");
163
164     // Update offset info and emplace
165     auto &parent_info = _parent_map[ind];
166     const auto &obj = _operands.at(ind);
167     auto parent_index = parent_info.parent;
168     auto &offset = parent_info.coordinates;
169     auto frontend_layout = parent_info.frontend_layout;
170
171     assert(obj.shape().rank() <= ir::Shape::MAX_RANK);
172     auto shape = obj.shape();
173     if (_operands.at(parent_index).shape().rank() >= 4 && frontend_layout == ir::Layout::NHWC &&
174         backend_layout == ir::Layout::NCHW)
175     {
176       // Permutation changing layout beyond 4-D is not supported yet
177       const auto parent_rank = _operands.at(parent_index).shape().rank();
178       assert(parent_rank == 4);
179       shape.extendRank(parent_rank);
180       offset = {offset[0], offset[3], offset[1], offset[2]};
181     }
182     else if (_operands.at(parent_index).shape().rank() >= 4 &&
183              frontend_layout == ir::Layout::NHWC && backend_layout == ir::Layout::NCHW)
184     {
185       // Permutation changing layout beyond 4-D is not supported yet
186       const auto parent_rank = _operands.at(parent_index).shape().rank();
187       assert(parent_rank == 4);
188       shape.extendRank(parent_rank);
189       offset = {offset[0], offset[2], offset[3], offset[1]};
190     }
191     auto new_shape = permuteShape(shape, frontend_layout, backend_layout);
192     auto oi = ir::OperandInfo::createStaticInfo(new_shape, obj.typeInfo());
193     _tensor_info_map.emplace(ind, oi);
194   }
195 }
196
197 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
198 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyFirstUse(const ir::OperandIndex &ind)
199 {
200   _lifetime_seq.emplace_back(UsesType::FIRST, ind);
201 }
202
203 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
204 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::notifyLastUse(const ir::OperandIndex &ind)
205 {
206   _lifetime_seq.emplace_back(UsesType::LAST, ind);
207 }
208
209 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
210 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isRegistered(
211     const ir::OperandIndex &ind) const
212 {
213   return _tensor_info_map.find(ind) != _tensor_info_map.end();
214 }
215
216 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
217 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::prepare(void)
218 {
219   buildTensors();
220 }
221
222 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
223 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::allocate(void)
224 {
225   // Update lifetime sequence to apply subtensor optimization
226
227   std::unordered_map<ir::OperandIndex, ir::OperandIndex> root_map;
228   std::function<ir::OperandIndex &(ir::OperandIndex)> find_root =
229       [&](ir::OperandIndex ind) -> ir::OperandIndex & {
230     ir::OperandIndex &ret = root_map[ind];
231
232     // We know the root parent value already
233     if (ret.valid())
234       return ret;
235
236     auto itr = _parent_map.find(ind);
237     if (itr == _parent_map.end())
238     {
239       // If there is no parent, let's store the value of itself
240       return ret = ind;
241     }
242     else
243     {
244       return ret = find_root(itr->second.parent);
245     }
246   };
247
248   ir::OperandIndexMap<bool> first_use_check;
249   ir::OperandIndexMap<bool> last_use_check;
250   std::map<size_t, std::pair<UsesType, ir::OperandIndex>> lifetime_map;
251   for (size_t i = 0; i < _lifetime_seq.size(); i++)
252   {
253     auto &entry = _lifetime_seq[i];
254     if (entry.first != UsesType::FIRST)
255       continue;
256     auto root_ind = find_root(entry.second);
257     if (first_use_check[root_ind])
258       continue;
259     first_use_check[root_ind] = true;
260     lifetime_map[i] = {UsesType::FIRST, root_ind};
261   }
262
263   for (int i = _lifetime_seq.size() - 1; i >= 0; i--)
264   {
265     auto &entry = _lifetime_seq[i];
266     if (entry.first != UsesType::LAST)
267       continue;
268     auto root_ind = find_root(entry.second);
269     if (last_use_check[root_ind])
270       continue;
271     last_use_check[root_ind] = true;
272     lifetime_map[i] = {UsesType::LAST, root_ind};
273   }
274
275   for (auto &entry : lifetime_map)
276   {
277     auto &use = entry.second;
278     auto use_type = use.first;
279     auto use_index = use.second;
280     assert(use_index.valid());
281     if (use_type == UsesType::FIRST)
282       _tensor_mgr->startLifetime(use_index);
283     else
284       _tensor_mgr->finishLifetime(use_index);
285   }
286
287   _tensor_mgr->allocateConsts();
288
289   // TODO Since `_parent_map` is filled for all Concat nodes even if the node this backend uses
290   //      After refactoring BackendContext we can uncomment this
291   // assert(_tensor_info_map.size() ==
292   //       _tensor_mgr->nonconstTensors().size() + num of constants of _tensor_info_map +
293   //       _parent_map.size());
294   _tensor_mgr->allocateNonconsts();
295
296   _tensor_mgr->allocateInternalBufferManager();
297 }
298
299 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
300 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::postFunctionPrepare(void)
301 {
302   _tensor_mgr->tryDeallocConstants();
303 }
304
305 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
306 void AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::buildTensors(void)
307 {
308   assert(_tensor_mgr->constTensors().size() == 0);
309   assert(_tensor_mgr->nonconstTensors().size() == 0);
310
311   // Normal tensors
312   for (auto &entry : _tensor_info_map)
313   {
314     auto ind = entry.first;
315     if (_parent_map.count(ind) > 0)
316       continue;
317
318     const auto &info = entry.second;
319     const auto &backend_layout = _tensor_layout_map[ind];
320     auto tensor_info =
321         asTensorInfo(info.shape(), info.typeInfo(), ir::Layout::UNKNOWN, backend_layout, true);
322     _tensor_mgr->buildTensor(ind, tensor_info, info.shape().rank(), info.isConstant(),
323                              _uses_count_map[ind]);
324   }
325
326   // Subtensors
327   assert(_tensor_mgr->nonconstSubtensors().size() == 0);
328   // TODO Iterate `_parent_map` instead, once the optimizer bug is fixed
329   //      `Optimizer` iterates the entire OpSequences, so there is a bug if iterating _parent_map
330   for (auto &entry : _tensor_info_map)
331   {
332     auto ind = entry.first;
333     if (_parent_map.count(ind) == 0)
334       continue;
335
336     // To make subtensor, parent tensor must be made first
337     // For this condition, use stack
338     //  1) Push one subtensor index to stack (iterate subtensors)
339     //  2) If tensor at stack top is already made, pop and go to 4)
340     //  3) If tensor pushed at 1) is not made, check parent tensor
341     //    3-1) If parent tensor is already made, we can make child tensor
342     //         Make child tensor and pop, go to 4)
343     //    3-2) If parent tensor is not made, we can't make child tensor yet
344     //         Push parent tensor index to stack and return to 4)
345     //  4) If stack is empty, return to 1), else return to 2)
346     auto &subtensors = _tensor_mgr->nonconstSubtensors();
347
348     std::stack<ir::OperandIndex> stack;
349     stack.push(ind);
350
351     while (!stack.empty())
352     {
353       const auto current = stack.top();
354       const auto &tensor_info = _tensor_info_map.at(current);
355       const auto &parent_info = _parent_map.at(current);
356
357       // Already generated SubTensor
358       if (subtensors.find(current) != subtensors.end())
359       {
360         stack.pop();
361         continue;
362       }
363
364       auto parent = parent_info.parent;
365       std::shared_ptr<T_ITensor> parent_tensor = _tensor_mgr->findTensorAsParent(parent);
366       if (!parent_tensor)
367       {
368         // Cannot find allocated parent tensor: allocate parent first
369         assert(_parent_map.count(parent) > 0);
370         stack.push(parent);
371         continue;
372       }
373       assert(parent_tensor != nullptr);
374
375       // Child's type should be same with parent
376       assert(tensor_info.typeInfo().offset() ==
377              parent_tensor->info()->quantization_info().uniform().offset);
378       assert(tensor_info.typeInfo().scale() ==
379              parent_tensor->info()->quantization_info().uniform().scale);
380       assert(tensor_info.typeInfo().type() == parent_tensor->data_type());
381
382       // NOTE SubTensor's layout must be the same with layout of parent tensor
383       const auto &root_parent = findRootParent(parent);
384       const auto &backend_layout = _tensor_layout_map[root_parent];
385
386       auto shape = asTensorShape(tensor_info.shape(), ir::Layout::UNKNOWN, backend_layout, true);
387       ::arm_compute::Coordinates coordinates =
388           asTensorCoordinate(parent_info.coordinates, ir::Layout::UNKNOWN, backend_layout);
389       _tensor_mgr->buildSubtensor(parent, current, shape, coordinates, tensor_info.shape().rank(),
390                                   true);
391       stack.pop();
392     }
393   }
394 }
395
396 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
397 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::areSubTensorsOf(
398     const ir::OperandIndex &parent, const ir::OperandIndexSequence &seq)
399 {
400   for (auto &cand : seq)
401   {
402     if (!isSubTensorOf(parent, cand))
403     {
404       return false;
405     }
406   }
407   return true;
408 }
409
410 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
411 bool AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::isSubTensorOf(
412     const ir::OperandIndex &parent, const ir::OperandIndex &child)
413 {
414   auto itr = _parent_map.find(child);
415   if (itr == _parent_map.end())
416   {
417     return false;
418   }
419
420   return itr->second.parent == parent;
421 }
422
423 template <typename T_ITensor, typename T_Tensor, typename T_SubTensor>
424 ir::OperandIndex
425 AclTensorBuilder<T_ITensor, T_Tensor, T_SubTensor>::findRootParent(ir::OperandIndex ind)
426 {
427   if (_parent_map.find(ind) == _parent_map.end())
428     return ind;
429
430   auto parent_ind = _parent_map.at(ind).parent;
431   return findRootParent(parent_ind);
432 }
433
434 } // namespace acl_common
435 } // namespace backend
436 } // namespace onert
437
438 #endif // __ONERT_BACKEND_ACL_COMMON_TEMPL_TENSOR_BUILDER_H__