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 184 of file GraphLoader.cpp.

188 : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir),
189 _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor),
190 _memory_manager(memory_manager)
191{
192}
T must_cast(loco::Node *node)

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 {
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
122
123 // Check single usage of input tensor
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);
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
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::must_cast(), 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 281 of file GraphLoader.cpp.

282{
283 auto input_nodes = loco::input_nodes(_graph);
284 std::vector<Tensor *> input_tensors(input_nodes.size());
285 for (size_t i = 0; i < input_nodes.size(); ++i)
286 {
287 input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
288 _memory_manager->allocate_memory(*input_tensors[i]);
289 }
290 _runtime_graph->setInputTensors(input_tensors);
291
292 auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
293 std::vector<Tensor *> output_tensors(output_nodes.size());
294 for (size_t i = 0; i < output_nodes.size(); ++i)
295 {
296 const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
297 output_tensors[i] = _node_to_tensor.at(node->from());
298 }
299 _runtime_graph->setOutputTensors(output_tensors);
300}
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
T must_cast(FeatureEncoder *node)
A helper dynamic_cast that throws when failed.
std::vector< Node * > output_nodes(Graph *)
Definition Graph.cpp:101

References luci_interpreter::IMemoryManager::allocate_memory(), loco::input_nodes(), loco::must_cast(), luci::must_cast(), 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 302 of file GraphLoader.cpp.

303{
304 KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor);
305
306 // Create kernels for executable nodes. This has to be done in execution order.
307 auto graph = const_cast<loco::Graph *>(_graph);
308
309 auto const graph_nodes = loco::all_nodes(graph);
310
311 // Checking for execution plan in node annotations.
312 bool has_execution_annotation = true;
313 auto const checking_exec_plan = [&has_execution_annotation](auto const node) {
317 };
319
321 {
322 // Build ordered_nodes vector that stores the order of execution of graph nodes.
323 std::vector<const luci::CircleNode *> ordered_nodes(graph_nodes.size());
324
325 auto const filler = [&ordered_nodes](auto const node) {
329 };
330 std::for_each(begin(graph_nodes), end(graph_nodes), filler);
331
332 for (auto node : ordered_nodes)
333 {
334 if (isExecutableNode(node))
335 {
336 std::unique_ptr<Kernel> kernel = kernel_builder.build(node);
337 _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
338 _runtime_graph->addKernel(std::move(kernel));
339 }
340 }
341 }
342 else
343 {
344 // If it is impossible to build the execution order plan,
345 // then we use the default postorder_traversal approach.
347 {
349 if (isExecutableNode(node))
350 {
351 std::unique_ptr<Kernel> kernel = kernel_builder.build(node);
352 _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
353 _runtime_graph->addKernel(std::move(kernel));
354 }
355 }
356 }
357}
Logical unit of computation.
Definition Node.h:55
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::get_execution_plan(), luci::has_execution_plan(), luci_interpreter::RuntimeToIR::kernel_to_node, loco::must_cast(), luci::must_cast(), 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 194 of file GraphLoader.cpp.

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