ONE - On-device Neural Engine
Loading...
Searching...
No Matches
luci_interpreter::GraphLoader Class Reference

#include <GraphLoader.h>

Public Member Functions

 GraphLoader (const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir, const std::unordered_map< const loco::Graph *, RuntimeGraph * > &graph_to_runtime_graph, std::unordered_map< const loco::Node *, Tensor * > &node_to_tensor, IMemoryManager *memory_manager)
 
void loadTensors ()
 
void initInputOutputTensors () const
 
void loadOperators ()
 

Static Public Member Functions

static void checkInplaceOps (CircleReader *reader, RuntimeGraph *runtime_graph)
 

Detailed Description

Definition at line 31 of file GraphLoader.h.

Constructor & Destructor Documentation

◆ GraphLoader()

luci_interpreter::GraphLoader::GraphLoader ( const loco::Graph graph,
RuntimeGraph runtime_graph,
RuntimeToIR runtime_to_ir,
const std::unordered_map< const loco::Graph *, RuntimeGraph * > &  graph_to_runtime_graph,
std::unordered_map< const loco::Node *, Tensor * > &  node_to_tensor,
IMemoryManager memory_manager 
)

Definition at line 177 of file GraphLoader.cpp.

181 : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir),
182 _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor),
183 _memory_manager(memory_manager)
184{
185}

Member Function Documentation

◆ checkInplaceOps()

void luci_interpreter::GraphLoader::checkInplaceOps ( CircleReader reader,
RuntimeGraph runtime_graph 
)
static

Definition at line 89 of file GraphLoader.cpp.

90{
91 const auto operators = reader->operators();
92 const auto graph_outputs = reader->outputs();
93 for (uint32_t i = 0; i < operators.size(); ++i)
94 {
95 const auto *op = operators.at(i);
96 assert(op != nullptr);
97
98 // Check inplace optimization for operation with single input and single output
99 if (isInplaceOperation(reader->builtin_code(op)))
100 {
101 const auto *op_inputs = op->inputs();
102 const auto *op_outputs = op->outputs();
103
104 bool is_inplace = true;
105 auto non_const_input_it = op_inputs->begin();
106 while (true)
107 {
108 non_const_input_it =
109 std::find_if(non_const_input_it, op_inputs->end(), [&reader](const auto input_idx) {
110 if (input_idx == -1)
111 return false;
112
113 return not Tensor::is_constant_tensor(reader, reader->tensors()[input_idx]);
114 });
115
116 if (non_const_input_it == op_inputs->end())
117 break;
118
119 auto dist = std::distance(op_inputs->begin(), non_const_input_it);
120
121 const auto non_const_input_idx = *non_const_input_it;
122
123 // Check single usage of input tensor
124 if (not isSingleUsageOfTensor(reader, non_const_input_idx))
125 {
126 is_inplace = false;
127 break;
128 }
129
130 // Let's check single usage of output tensor
131 if (dist >= op_outputs->size() and op_outputs->size() == 1)
132 dist = 0;
133 assert(dist < op_outputs->size());
134 const auto output_index = op_outputs->operator[](dist);
135 if (not isSingleUsageOfTensor(reader, output_index))
136 {
137 is_inplace = false;
138 break;
139 }
140
141 // Check that num elements are equal
142 {
143 const auto *input_non_const_tensor = reader->tensors().at(non_const_input_idx);
144 const auto *output_tensor = reader->tensors().at(output_index);
145 if (Tensor::num_elements(input_non_const_tensor) != Tensor::num_elements(output_tensor))
146 {
147 is_inplace = false;
148 break;
149 }
150 }
151
152 // Let's check that output is not a graph output tensor
153 // TODO: check this statement
154 {
155 if (std::find(graph_outputs.begin(), graph_outputs.end(), output_index) !=
156 graph_outputs.end())
157 {
158 is_inplace = false;
159 break;
160 }
161 }
162
163 non_const_input_it++;
164 }
165
166 if (is_inplace)
167 runtime_graph->addInplaceOpIndex(op);
168 }
169 }
170}
int32_t size[5]
Definition Slice.cpp:35

References luci_interpreter::RuntimeGraph::addInplaceOpIndex(), luci::VectorWrapper< T >::at(), luci::CircleReader::builtin_code(), luci::CircleReader::operators(), luci::CircleReader::outputs(), size, and luci::CircleReader::tensors().

Referenced by luci_interpreter::ModuleLoader::load().

◆ initInputOutputTensors()

void luci_interpreter::GraphLoader::initInputOutputTensors ( ) const

Definition at line 274 of file GraphLoader.cpp.

275{
276 auto input_nodes = loco::input_nodes(_graph);
277 std::vector<Tensor *> input_tensors(input_nodes.size());
278 for (size_t i = 0; i < input_nodes.size(); ++i)
279 {
280 input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
281 _memory_manager->allocate_memory(*input_tensors[i]);
282 }
283 _runtime_graph->setInputTensors(input_tensors);
284
285 auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
286 std::vector<Tensor *> output_tensors(output_nodes.size());
287 for (size_t i = 0; i < output_nodes.size(); ++i)
288 {
289 const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
290 output_tensors[i] = _node_to_tensor.at(node->from());
291 }
292 _runtime_graph->setOutputTensors(output_tensors);
293}
A neural network graph.
Definition Graph.h:161
virtual void allocate_memory(luci_interpreter::Tensor &tensor)=0
void setOutputTensors(const std::vector< Tensor * > &output_tensors)
void setInputTensors(const std::vector< Tensor * > &input_tensors)
std::vector< Node * > input_nodes(const Graph *)
Definition Graph.cpp:71
std::vector< Node * > output_nodes(Graph *)
Definition Graph.cpp:101

References luci_interpreter::IMemoryManager::allocate_memory(), loco::input_nodes(), loco::output_nodes(), luci_interpreter::RuntimeGraph::setInputTensors(), and luci_interpreter::RuntimeGraph::setOutputTensors().

Referenced by luci_interpreter::ModuleLoader::load().

◆ loadOperators()

void luci_interpreter::GraphLoader::loadOperators ( )

Definition at line 295 of file GraphLoader.cpp.

296{
297 KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor);
298
299 // Create kernels for executable nodes. This has to be done in execution order.
300 auto graph = const_cast<loco::Graph *>(_graph);
301
302 auto const graph_nodes = loco::all_nodes(graph);
303
304 // Checking for execution plan in node annotations.
305 bool has_execution_annotation = true;
306 auto const checking_exec_plan = [&has_execution_annotation](auto const node) {
307 const auto *circle_node = loco::must_cast<const luci::CircleNode *>(node);
308 if (!luci::has_execution_plan(circle_node))
309 has_execution_annotation = false;
310 };
311 std::for_each(begin(graph_nodes), end(graph_nodes), checking_exec_plan);
312
313 if (has_execution_annotation)
314 {
315 // Build ordered_nodes vector that stores the order of execution of graph nodes.
316 std::vector<const luci::CircleNode *> ordered_nodes(graph_nodes.size());
317
318 auto const filler = [&ordered_nodes](auto const node) {
319 const auto *circle_node = loco::must_cast<const luci::CircleNode *>(node);
320 auto const position = luci::get_execution_plan(circle_node).order_in_plan();
321 ordered_nodes.at(position) = circle_node;
322 };
323 std::for_each(begin(graph_nodes), end(graph_nodes), filler);
324
325 for (auto node : ordered_nodes)
326 {
327 if (isExecutableNode(node))
328 {
329 std::unique_ptr<Kernel> kernel = kernel_builder.build(node);
330 _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
331 _runtime_graph->addKernel(std::move(kernel));
332 }
333 }
334 }
335 else
336 {
337 // If it is impossible to build the execution order plan,
338 // then we use the default postorder_traversal approach.
339 for (const loco::Node *loco_node : loco::postorder_traversal(loco::output_nodes(graph)))
340 {
341 const auto *node = loco::must_cast<const luci::CircleNode *>(loco_node);
342 if (isExecutableNode(node))
343 {
344 std::unique_ptr<Kernel> kernel = kernel_builder.build(node);
345 _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
346 _runtime_graph->addKernel(std::move(kernel));
347 }
348 }
349 }
350}
Logical unit of computation.
Definition Node.h:54
void addKernel(std::unique_ptr< Kernel > &&kernel)
std::vector< loco::Node * > postorder_traversal(const std::vector< loco::Node * > &roots)
Generate postorder traversal sequence starting from "roots".
Definition Algorithm.cpp:53
std::set< Node * > all_nodes(Graph *)
Enumerate all the nodes in a given graph.
Definition Graph.cpp:59
bool has_execution_plan(const luci::CircleNode *circle_node)
luci::CircleNodeExecutionPlan get_execution_plan(const luci::CircleNode *circle_node)
ShapeIterator end(const Shape &s)
int32_t begin[5]
Definition Slice.cpp:33
std::unordered_map< const Kernel *, const luci::CircleNode * > kernel_to_node
Definition RuntimeToIR.h:33

References luci_interpreter::RuntimeGraph::addKernel(), loco::all_nodes(), begin, luci_interpreter::KernelBuilder::build(), luci::get_execution_plan(), luci::has_execution_plan(), luci_interpreter::RuntimeToIR::kernel_to_node, luci::CircleNodeExecutionPlan::order_in_plan(), loco::output_nodes(), and loco::postorder_traversal().

Referenced by luci_interpreter::ModuleLoader::load().

◆ loadTensors()

void luci_interpreter::GraphLoader::loadTensors ( )

Definition at line 187 of file GraphLoader.cpp.

188{
189 for (uint32_t i = 0; i < _graph->nodes()->size(); ++i)
190 {
191 const auto *node = loco::must_cast<const luci::CircleNode *>(_graph->nodes()->at(i));
192
193 if (node->opcode() == luci::CircleOpcode::CUSTOM && !isSupportedCustomNode(node))
194 {
195 const auto *cnode = loco::must_cast<const luci::CircleCustom *>(node);
196 throw std::runtime_error("Unsupported Custom operator. " + cnode->custom_code() + " in " +
197 node->name());
198 }
199
200 if (!isTensorProducingNode(node))
201 continue;
202
203 // Only Input, Const, Custom and Variable nodes have shapes. Shapes of intermediate tensors will
204 // be inferred.
205 Shape shape{};
206 switch (node->opcode())
207 {
208 case luci::CircleOpcode::CIRCLECONST:
209 case luci::CircleOpcode::CIRCLECUSTOMOUT:
210 case luci::CircleOpcode::CIRCLEINPUT:
211 case luci::CircleOpcode::CIRCLEVARIABLE:
212 shape = getNodeShape(node);
213 break;
214 default:
215 break;
216 }
217
218 AffineQuantization quantization;
219 if (node->quantparam() != nullptr)
220 {
221 const luci::CircleQuantParam *params = node->quantparam();
222 assert(params->scale.size() == params->zerop.size());
223 quantization.scale.assign(params->scale.cbegin(), params->scale.cend());
224 quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend());
225 quantization.quantized_dimension = params->quantized_dimension;
226 }
227
228 auto tensor = std::make_unique<Tensor>(node->dtype(), std::move(shape), std::move(quantization),
229 node->name());
230
231 // If node has execution plan then read memory offsets for nodes
232 // from the beginning of shared memory buffer. Used in Static Memory Manager.
233 if (luci::has_execution_plan(node))
234 {
235 auto execution_plan = luci::get_execution_plan(node);
236 assert(!execution_plan.offsets().empty());
237 tensor->set_offset(execution_plan.offsets().front());
238 }
239
240 if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
241 {
242 size_t data_size{};
243 const void *const_data = getNodeData(const_node, &data_size);
244 if (const_data != nullptr)
245 {
246 _memory_manager->allocate_memory(*tensor);
247 tensor->writeData(const_data, data_size);
248 }
249 }
250 else if (const auto *custom_out_node = dynamic_cast<const luci::CircleCustomOut *>(node))
251 {
252 const auto *custom_node =
253 loco::must_cast<const luci::CircleCustom *>(custom_out_node->input());
254
255 if (custom_node->custom_code() == "CircleReferencingConst")
256 {
257 size_t data_size{};
258 const void *const_data = getNodeData(custom_node, &data_size);
259 if (const_data != nullptr)
260 {
261 _memory_manager->allocate_memory(*tensor);
262 tensor->writeData(const_data, data_size);
263 }
264 }
265 }
266
267 _node_to_tensor.emplace(node, tensor.get());
268 _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node);
269
270 _runtime_graph->addTensor(std::move(tensor));
271 }
272}
NodeContext * nodes(void)
Definition Graph.h:218
T * at(uint32_t n) const
Access N-th object.
Definition ObjectPool.h:41
Class to build tensor data.
Definition CircleConst.h:35
Virtual CIRCLECUSTOMOUT in Circle.
Tensor * addTensor(std::unique_ptr< Tensor > &&tensor)
Definition Shape.h:28
std::vector< float > scale
std::vector< int64_t > zerop
std::unordered_map< const Tensor *, const luci::CircleNode * > tensor_to_node
Definition RuntimeToIR.h:32

References luci_interpreter::RuntimeGraph::addTensor(), luci_interpreter::IMemoryManager::allocate_memory(), loco::ObjectPool< T >::at(), luci::get_execution_plan(), luci::has_execution_plan(), luci::CircleNode::name(), loco::Graph::nodes(), luci::CircleNode::opcode(), luci_interpreter::AffineQuantization::quantized_dimension, luci::CircleQuantParam::quantized_dimension, luci::CircleNode::quantparam(), luci_interpreter::AffineQuantization::scale, luci::CircleQuantParam::scale, size, luci_interpreter::RuntimeToIR::tensor_to_node, luci_interpreter::AffineQuantization::zero_point, and luci::CircleQuantParam::zerop.

Referenced by luci_interpreter::ModuleLoader::load().


The documentation for this class was generated from the following files: