ONE - On-device Neural Engine
Loading...
Searching...
No Matches
RuntimeGraph.cpp
Go to the documentation of this file.
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
24namespace luci_interpreter
25{
26
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
34public:
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
44 : _memory_manager(memory_manager)
45{
46}
47
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
85void 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
94void 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
104 : _owning_module(owning_module), _memory_manager(memory_manager),
105 _tensor_alloc_plan(std::make_unique<TensorAllocPlan>(memory_manager))
106{
107}
108
110{
111 for (auto &tensor : _tensors)
112 {
113 if (tensor->is_data_allocated())
114 _memory_manager->release_memory(*tensor);
115 }
116}
117
118Tensor *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
125void 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
132void 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
140{
141 _memory_manager->allocate_memory(*tensor);
142}
143
144void 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
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
virtual void postOperatorExecute(const Kernel *kernel)=0
virtual void postTensorWrite(const Tensor *tensor)=0
virtual void preOperatorExecute(const Kernel *kernel)=0
virtual void release_memory(luci_interpreter::Tensor &tensor)=0
virtual void allocate_memory(luci_interpreter::Tensor &tensor)=0
void allocate(size_t kernel_index) const
void deallocate(size_t kernel_index) const
TensorAllocPlan(IMemoryManager *memory_manager)
void addKernel(std::unique_ptr< Kernel > &&kernel)
Tensor * addTensor(std::unique_ptr< Tensor > &&tensor)
void configureAllocations(Tensor *tensor)
void setOutputTensors(const std::vector< Tensor * > &output_tensors)
const std::vector< Tensor * > & getInputTensors() const
void setInputTensors(const std::vector< Tensor * > &input_tensors)
EventNotifier * getEventNotifier() const