Imported Upstream version 1.21.0
[platform/core/ml/nnfw.git] / compiler / luci-micro / luci-interpreter / src / core / RuntimeGraph.cpp
1 /*
2  * Copyright (c) 2020 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 "core/RuntimeGraph.h"
18
19 #include "core/RuntimeModule.h"
20
21 #include <algorithm>
22 #include <unordered_map>
23
24 namespace luci_interpreter
25 {
26
27 class RuntimeGraph::TensorAllocPlan
28 {
29   std::vector<std::vector<Tensor *>> _alloc_plan;
30   std::vector<std::vector<Tensor *>> _dealloc_plan;
31   bool _valid = false;
32   IMemoryManager *_memory_manager;
33
34 public:
35   explicit TensorAllocPlan(IMemoryManager *memory_manager);
36   void invalidate() { _valid = false; }
37   bool isValid() const { return _valid; }
38   void build(const RuntimeGraph &graph);
39   void allocate(size_t kernel_index) const;
40   void deallocate(size_t kernel_index) const;
41 };
42
43 RuntimeGraph::TensorAllocPlan::TensorAllocPlan(IMemoryManager *memory_manager)
44   : _memory_manager(memory_manager)
45 {
46 }
47
48 void RuntimeGraph::TensorAllocPlan::build(const RuntimeGraph &graph)
49 {
50   invalidate();
51   using Lifetime = std::pair<size_t, size_t>;
52   std::unordered_map<Tensor *, Lifetime> lifetimes;
53   const size_t num_kernels = graph._kernels.size();
54   for (size_t index = 0; index < num_kernels; ++index)
55   {
56     const auto &kernel = graph._kernels[index];
57     for (const Tensor *tensor : kernel->getInputTensors())
58     {
59       auto nc_tensor = const_cast<Tensor *>(tensor);
60       if (lifetimes.count(nc_tensor) > 0)
61         lifetimes.at(nc_tensor).second = index;
62     }
63     for (Tensor *tensor : kernel->getOutputTensors())
64     {
65       assert(lifetimes.count(tensor) == 0);
66       lifetimes[tensor] = Lifetime(index, index);
67     }
68   }
69   for (const Tensor *tensor : graph.getOutputTensors())
70   {
71     auto nc_tensor = const_cast<Tensor *>(tensor);
72     if (lifetimes.count(nc_tensor) > 0)
73       lifetimes.at(nc_tensor).second = num_kernels;
74   }
75   _alloc_plan.assign(num_kernels, std::vector<Tensor *>());
76   _dealloc_plan.assign(num_kernels + 1, std::vector<Tensor *>());
77   for (const auto &item : lifetimes)
78   {
79     _alloc_plan[item.second.first].push_back(item.first);
80     _dealloc_plan[item.second.second].push_back(item.first);
81   }
82   _valid = true;
83 }
84
85 void RuntimeGraph::TensorAllocPlan::allocate(size_t kernel_index) const
86 {
87   assert(_valid && kernel_index < _alloc_plan.size());
88   for (Tensor *tensor : _alloc_plan[kernel_index])
89   {
90     _memory_manager->allocate_memory(*tensor);
91   }
92 }
93
94 void RuntimeGraph::TensorAllocPlan::deallocate(size_t kernel_index) const
95 {
96   assert(_valid && kernel_index < _dealloc_plan.size());
97   for (Tensor *tensor : _dealloc_plan[kernel_index])
98   {
99     _memory_manager->release_memory(*tensor);
100   }
101 }
102
103 RuntimeGraph::RuntimeGraph(RuntimeModule *owning_module, IMemoryManager *memory_manager)
104   : _owning_module(owning_module), _memory_manager(memory_manager),
105     _tensor_alloc_plan(std::make_unique<TensorAllocPlan>(memory_manager))
106 {
107 }
108
109 RuntimeGraph::~RuntimeGraph()
110 {
111   for (auto &tensor : _tensors)
112   {
113     if (tensor->is_data_allocated())
114       _memory_manager->release_memory(*tensor);
115   }
116 }
117
118 Tensor *RuntimeGraph::addTensor(std::unique_ptr<Tensor> &&tensor)
119 {
120   assert(tensor != nullptr);
121   _tensors.push_back(std::move(tensor));
122   return _tensors.back().get();
123 }
124
125 void RuntimeGraph::setInputTensors(const std::vector<Tensor *> &input_tensors)
126 {
127   assert(std::all_of(input_tensors.cbegin(), input_tensors.cend(),
128                      [](Tensor *tensor) { return tensor != nullptr; }));
129   _input_tensors = input_tensors;
130 }
131
132 void RuntimeGraph::setOutputTensors(const std::vector<Tensor *> &output_tensors)
133 {
134   assert(std::all_of(output_tensors.cbegin(), output_tensors.cend(),
135                      [](Tensor *tensor) { return tensor != nullptr; }));
136   _output_tensors = output_tensors;
137 }
138
139 void RuntimeGraph::configureAllocations(Tensor *tensor)
140 {
141   _memory_manager->allocate_memory(*tensor);
142 }
143
144 void RuntimeGraph::addKernel(std::unique_ptr<Kernel> &&kernel)
145 {
146   assert(kernel != nullptr);
147   _kernels.push_back(std::move(kernel));
148   _tensor_alloc_plan->invalidate();
149 }
150
151 void RuntimeGraph::execute() const
152 {
153   if (!_tensor_alloc_plan->isValid())
154     _tensor_alloc_plan->build(*this);
155
156   EventNotifier *event_notifier = _owning_module->getEventNotifier();
157
158   // Notify the observers that the input tensors have changed.
159   if (event_notifier != nullptr)
160   {
161     for (const Tensor *input_tensor : getInputTensors())
162     {
163       if (input_tensor->is_observable())
164         event_notifier->postTensorWrite(input_tensor);
165     }
166   }
167
168   for (size_t index = 0; index < _kernels.size(); ++index)
169   {
170     const auto &kernel = _kernels[index];
171     if (event_notifier != nullptr)
172     {
173       event_notifier->preOperatorExecute(kernel.get());
174     }
175
176     // TODO The `configure` method should only be called if the outputs of an operator need to be
177     //  resized.
178     kernel->configure();
179
180     // Preallocate outputs in advance instead of relying on automatic allocation
181     _tensor_alloc_plan->allocate(index);
182
183     kernel->execute();
184
185     if (event_notifier != nullptr)
186     {
187       event_notifier->postOperatorExecute(kernel.get());
188     }
189
190     for (const Tensor *tensor : kernel->getOutputTensors())
191     {
192       if (event_notifier != nullptr && tensor->is_observable())
193       {
194         event_notifier->postTensorWrite(tensor);
195       }
196     }
197     _tensor_alloc_plan->deallocate(index);
198   }
199 }
200
201 } // namespace luci_interpreter