Compute Library
 22.11
arm_compute::graph::detail Namespace Reference

Functions

void configure_transition_manager (Graph &g, GraphContext &ctx, ExecutionWorkload &workload)
 Configures transition manager and execution workload. More...
 
void validate_all_nodes (Graph &g)
 Validates all nodes. More...
 
void configure_all_tensors (Graph &g)
 Configures all nodes of a graph. More...
 
void allocate_all_input_tensors (INode &node)
 Allocates all input tensors of a node. More...
 
void allocate_all_output_tensors (INode &node)
 Allocates all output tensors of a node. More...
 
void allocate_const_tensors (Graph &g)
 Allocates const tensor of a given graph. More...
 
void allocate_all_tensors (Graph &g)
 Allocates all tensors of a graph. More...
 
ExecutionWorkload configure_all_nodes (Graph &g, GraphContext &ctx, const std::vector< NodeID > &node_order)
 Configures all nodes of graph. More...
 
void release_unused_tensors (Graph &g)
 Release the memory of all unused const nodes. More...
 
void call_tensor_accessor (Tensor *tensor)
 Calls accessor of a given tensor. More...
 
void call_all_const_node_accessors (Graph &g)
 Call all const node accessors. More...
 
bool call_all_input_node_accessors (ExecutionWorkload &workload)
 Call all input node accessors. More...
 
bool call_all_output_node_accessors (ExecutionWorkload &workload)
 Call all output node accessors. More...
 
void prepare_all_tasks (ExecutionWorkload &workload)
 Prepares all tasks for execution. More...
 
void call_all_tasks (ExecutionWorkload &workload)
 Executes all tasks of a workload. More...
 
bool all_inputs_are_visited (const INode *node, const std::vector< bool > &visited)
 Checks if all the input dependencies of a node have been visited. More...
 
void transfer_driving_nodes_and_remove_old_node (Graph &g, INode *new_node, INode *old_node, bool add_output_tensor)
 
void fuse_convolution_with_batch_normalization (Graph &g, const Edge *output_edge)
 
void fuse_depthwise_convolution_with_batch_normalization (Graph &g, const Edge *output_edge)
 
template<typename N >
void fuse_node_with_activation (Graph &g, const Edge *output_edge, const std::set< Activation > &supported_fused_activations)
 
template<typename N >
void fuse_pad_with_convolution (Graph &g, const Edge *output_edge)
 
template<typename N1 , typename N2 , typename F , typename... Args>
void fuse_layer (Graph &g, std::function< bool(INode &)> const &prec, const F fuse_fcn, Args &&... optional_arguments)
 
bool check_post_op_type (NodeType *post_op_type, int len)
 
void fuse_convolution_with_post_op (Graph &g, INode *fused_node, std::list< INode *> post_op_node_list, int prev_op_dst_pos)
 
std::list< INode * > get_post_op_list (Graph &g, int &eltwise_operand_id, int &prev_op_dst_pos, unsigned int conv_node_id, const std::set< Activation > &supported_fused_activations)
 
void fuse_convolution_with_post_ops (Graph &g, const Edge *output_edge, unsigned int conv_node_id, const std::set< Activation > &supported_fused_activations)
 Fuse below operators: More...
 
void fuse_convolution_batch_normalization_with_post_ops (Graph &g, const Edge *output_edge, unsigned int conv_node_id, const std::set< Activation > &supported_fused_activations)
 
template<typename N1 , typename F , typename... Args>
void fuse_layer (Graph &g, std::function< bool(INode &)> const &prec, const F fuse_fcn, Args &&... optional_arguments)
 

Variables

NodeType valide_post_op_type [4][3]
 

Function Documentation

◆ all_inputs_are_visited()

bool arm_compute::graph::detail::all_inputs_are_visited ( const INode node,
const std::vector< bool > &  visited 
)
inline

Checks if all the input dependencies of a node have been visited.

Parameters
[in]nodeNode to check
[in]visitedVector that contains the visited information
Returns
True if all inputs dependencies have been visited else false

Definition at line 46 of file TopologicalSort.cpp.

References ARM_COMPUTE_ERROR_ON, Graph::edge(), arm_compute::graph::EmptyNodeID, INode::graph(), INode::input_edges(), Edge::producer(), and Edge::producer_id().

Referenced by arm_compute::graph::bfs(), and arm_compute::graph::dfs().

47 {
48  ARM_COMPUTE_ERROR_ON(node == nullptr);
49  const Graph *graph = node->graph();
50  ARM_COMPUTE_ERROR_ON(graph == nullptr);
51 
52  bool are_all_visited = true;
53  for(const auto &input_edge_id : node->input_edges())
54  {
55  if(input_edge_id != EmptyNodeID)
56  {
57  const Edge *input_edge = graph->edge(input_edge_id);
58  ARM_COMPUTE_ERROR_ON(input_edge == nullptr);
59  ARM_COMPUTE_ERROR_ON(input_edge->producer() == nullptr);
60  if(!visited[input_edge->producer_id()])
61  {
62  are_all_visited = false;
63  break;
64  }
65  }
66  }
67 
68  return are_all_visited;
69 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
constexpr NodeID EmptyNodeID
Constant EdgeID specifying an equivalent of null edge.
Definition: Types.h:76

◆ allocate_all_input_tensors()

void allocate_all_input_tensors ( INode node)

Allocates all input tensors of a node.

Parameters
[in]nodeNode to allocate the input tensor of

Definition at line 73 of file ExecutionHelpers.cpp.

References ITensorHandle::allocate(), ARM_COMPUTE_ERROR_ON_MSG, Tensor::bound_edges(), Tensor::handle(), INode::input(), INode::num_inputs(), and tensor.

Referenced by allocate_const_tensors().

74 {
75  for(unsigned int i = 0; i < node.num_inputs(); ++i)
76  {
77  Tensor *tensor = node.input(i);
78  if(tensor != nullptr && !tensor->bound_edges().empty())
79  {
80  ARM_COMPUTE_ERROR_ON_MSG(!tensor->handle(), "Tensor handle is not configured!");
81  tensor->handle()->allocate();
82  }
83  }
84 }
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ allocate_all_output_tensors()

void allocate_all_output_tensors ( INode node)

Allocates all output tensors of a node.

Parameters
[in]nodeNode to allocate the output tensor of

Definition at line 86 of file ExecutionHelpers.cpp.

References ITensorHandle::allocate(), ARM_COMPUTE_ERROR_ON_MSG, Tensor::bound_edges(), Tensor::handle(), INode::num_outputs(), INode::output(), and tensor.

Referenced by allocate_const_tensors().

87 {
88  for(unsigned int i = 0; i < node.num_outputs(); ++i)
89  {
90  Tensor *tensor = node.output(i);
91  if(tensor != nullptr && !tensor->bound_edges().empty())
92  {
93  ARM_COMPUTE_ERROR_ON_MSG(!tensor->handle(), "Tensor handle is not configured!");
94  tensor->handle()->allocate();
95  }
96  }
97 }
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ allocate_all_tensors()

void allocate_all_tensors ( Graph g)

Allocates all tensors of a graph.

Parameters
[in]gGraph to allocate the tensors

Definition at line 120 of file ExecutionHelpers.cpp.

References tensor, and Graph::tensors().

Referenced by GraphManager::finalize_graph().

121 {
122  auto &tensors = g.tensors();
123 
124  for(auto &tensor : tensors)
125  {
126  if(tensor && !tensor->bound_edges().empty() && tensor->handle() != nullptr && tensor->handle()->tensor().info()->is_resizable() && tensor->handle()->tensor().is_used())
127  {
128  tensor->handle()->allocate();
129  }
130  }
131 }
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ allocate_const_tensors()

void allocate_const_tensors ( Graph g)

Allocates const tensor of a given graph.

Parameters
[in]gGraph to allocate the tensors

Definition at line 99 of file ExecutionHelpers.cpp.

References allocate_all_input_tensors(), allocate_all_output_tensors(), arm_compute::graph::Const, arm_compute::graph::Input, Graph::nodes(), and arm_compute::graph::Output.

Referenced by GraphManager::finalize_graph().

100 {
101  for(auto &node : g.nodes())
102  {
103  if(node != nullptr)
104  {
105  switch(node->type())
106  {
107  case NodeType::Const:
108  case NodeType::Input:
110  break;
111  case NodeType::Output:
113  default:
114  break;
115  }
116  }
117  }
118 }
void allocate_all_input_tensors(INode &node)
Allocates all input tensors of a node.
void allocate_all_output_tensors(INode &node)
Allocates all output tensors of a node.

◆ call_all_const_node_accessors()

void call_all_const_node_accessors ( Graph g)

Call all const node accessors.

Parameters
[in]gGraph containing the const nodes

Definition at line 193 of file ExecutionHelpers.cpp.

References call_tensor_accessor(), arm_compute::graph::Const, and Graph::nodes().

Referenced by GraphManager::finalize_graph().

194 {
195  auto &nodes = g.nodes();
196 
197  for(auto &node : nodes)
198  {
199  if(node != nullptr && node->type() == NodeType::Const && node->num_outputs())
200  {
201  if(!node->output(0)->bound_edges().empty())
202  {
203  call_tensor_accessor(node->output(0));
204  }
205  }
206  }
207 }
void call_tensor_accessor(Tensor *tensor)
Calls accessor of a given tensor.

◆ call_all_input_node_accessors()

bool call_all_input_node_accessors ( ExecutionWorkload workload)

Call all input node accessors.

Parameters
[in]workloadWorkload to execute
Returns
True if all the accesses were valid

Definition at line 209 of file ExecutionHelpers.cpp.

References arm_compute::mlgo::parser::end(), arm_compute::utility::for_each(), and ExecutionWorkload::inputs.

Referenced by GraphManager::execute_graph().

210 {
211  bool is_valid = true;
212  std::for_each(std::begin(workload.inputs), std::end(workload.inputs), [&](Tensor * input_tensor)
213  {
214  bool valid_input = (input_tensor != nullptr) && input_tensor->call_accessor();
215  is_valid = is_valid && valid_input;
216  });
217  return is_valid;
218 }
void end(TokenStream &in, bool &valid)
Definition: MLGOParser.cpp:290
void for_each(F &&)
Base case of for_each.
Definition: Utility.h:110

◆ call_all_output_node_accessors()

bool call_all_output_node_accessors ( ExecutionWorkload workload)

Call all output node accessors.

Parameters
[in]workloadWorkload to execute
Returns
True if all the accessors expect more data

Definition at line 259 of file ExecutionHelpers.cpp.

References arm_compute::mlgo::parser::end(), arm_compute::utility::for_each(), ExecutionWorkload::outputs, and arm_compute::graph::sync_backends().

Referenced by GraphManager::execute_graph().

260 {
261  bool is_valid = true;
262  std::for_each(std::begin(workload.outputs), std::end(workload.outputs), [&](Tensor * output_tensor)
263  {
264  bool valid_output = (output_tensor != nullptr) && output_tensor->call_accessor();
265  is_valid = is_valid && valid_output;
266  });
267 
268  sync_backends();
269 
270  return is_valid;
271 }
void end(TokenStream &in, bool &valid)
Definition: MLGOParser.cpp:290
void for_each(F &&)
Base case of for_each.
Definition: Utility.h:110
void sync_backends()
Synchronize kernels execution on the backends.
Definition: Utils.cpp:119

◆ call_all_tasks()

void call_all_tasks ( ExecutionWorkload workload)

Executes all tasks of a workload.

Parameters
[in]workloadWorkload to execute

Definition at line 230 of file ExecutionHelpers.cpp.

References ARM_COMPUTE_ERROR_ON, ExecutionWorkload::ctx, GraphContext::memory_managers(), and ExecutionWorkload::tasks.

Referenced by GraphManager::execute_graph().

231 {
232  ARM_COMPUTE_ERROR_ON(workload.ctx == nullptr);
233 
234  // Acquire memory for the transition buffers
235  for(auto &mm_ctx : workload.ctx->memory_managers())
236  {
237  if(mm_ctx.second.cross_group != nullptr)
238  {
239  mm_ctx.second.cross_group->acquire();
240  }
241  }
242 
243  // Execute tasks
244  for(auto &task : workload.tasks)
245  {
246  task();
247  }
248 
249  // Release memory for the transition buffers
250  for(auto &mm_ctx : workload.ctx->memory_managers())
251  {
252  if(mm_ctx.second.cross_group != nullptr)
253  {
254  mm_ctx.second.cross_group->release();
255  }
256  }
257 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466

◆ call_tensor_accessor()

void call_tensor_accessor ( Tensor tensor)

Calls accessor of a given tensor.

Parameters
[in]tensorThe tensor of which the accessor should be called

Definition at line 187 of file ExecutionHelpers.cpp.

References ARM_COMPUTE_ERROR_ON, and Tensor::call_accessor().

Referenced by call_all_const_node_accessors().

188 {
190  tensor->call_accessor();
191 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ check_post_op_type()

bool arm_compute::graph::detail::check_post_op_type ( NodeType post_op_type,
int  len 
)

Definition at line 353 of file NodeFusionMutator.cpp.

References MAX_POST_OP_NUM, and MAX_VALIDE_COMBINATION.

Referenced by get_post_op_list().

354 {
355  if(len > MAX_POST_OP_NUM || len <= 0)
356  {
357  return false;
358  }
359 
360  bool found = false;
361  for(int i = 0; i < MAX_VALIDE_COMBINATION; ++i)
362  {
363  for(int j = 0; j < len; ++j)
364  {
365  if(post_op_type[j] != valide_post_op_type[i][j])
366  {
367  found = false;
368  break;
369  }
370  found = true;
371  }
372  if(found)
373  break;
374  }
375 
376  return found;
377 }
#define MAX_POST_OP_NUM
#define MAX_VALIDE_COMBINATION
Check valid combinations:

◆ configure_all_nodes()

ExecutionWorkload configure_all_nodes ( Graph g,
GraphContext ctx,
const std::vector< NodeID > &  node_order 
)

Configures all nodes of graph.

Parameters
[in,out]gGraph to configure the nodes
[in]ctxGraph context to use
[in]node_orderThe order to configure the nodes
Returns
The execution workload

Definition at line 133 of file ExecutionHelpers.cpp.

References INode::assigned_target(), IDeviceBackend::configure_node(), ExecutionWorkload::ctx, BackendRegistry::get(), BackendRegistry::get_backend(), ExecutionWorkload::graph, arm_compute::graph::Input, ExecutionWorkload::inputs, arm_compute::graph::is_utility_node(), Graph::node(), Graph::nodes(), arm_compute::graph::Output, ExecutionWorkload::outputs, ExecutionWorkload::tasks, and arm_compute::test::validation::workload.

Referenced by GraphManager::finalize_graph().

134 {
135  ExecutionWorkload workload;
136  workload.graph = &g;
137  workload.ctx = &ctx;
138 
139  // Reserve memory for tasks
140  workload.tasks.reserve(node_order.size());
141 
142  // Create tasks
143  for(auto &node_id : node_order)
144  {
145  auto node = g.node(node_id);
146  if(node != nullptr)
147  {
148  Target assigned_target = node->assigned_target();
149  backends::IDeviceBackend &backend = backends::BackendRegistry::get().get_backend(assigned_target);
150  std::unique_ptr<IFunction> func = backend.configure_node(*node, ctx);
151  if(func != nullptr || is_utility_node(node))
152  {
153  workload.tasks.emplace_back(ExecutionTask(std::move(func), node));
154  }
155  }
156  }
157 
158  // Add inputs and outputs
159  for(auto &node : g.nodes())
160  {
161  if(node != nullptr && node->type() == NodeType::Input)
162  {
163  workload.inputs.push_back(node->output(0));
164  }
165 
166  if(node != nullptr && node->type() == NodeType::Output)
167  {
168  workload.outputs.push_back(node->input(0));
169  continue;
170  }
171  }
172 
173  return workload;
174 }
bool is_utility_node(INode *node)
Definition: Utils.h:37
Target
< Target enum
Definition: Acl.hpp:293

◆ configure_all_tensors()

void configure_all_tensors ( Graph g)

Configures all nodes of a graph.

Parameters
[in]gGraph to configure

Definition at line 56 of file ExecutionHelpers.cpp.

References ARM_COMPUTE_ERROR_ON_MSG, IDeviceBackend::create_tensor(), BackendRegistry::get(), BackendRegistry::get_backend(), tensor, and Graph::tensors().

Referenced by GraphManager::finalize_graph().

57 {
58  auto &tensors = g.tensors();
59 
60  for(auto &tensor : tensors)
61  {
62  if(tensor && tensor->handle() == nullptr)
63  {
64  Target target = tensor->desc().target;
65  backends::IDeviceBackend &backend = backends::BackendRegistry::get().get_backend(target);
66  std::unique_ptr<ITensorHandle> handle = backend.create_tensor(*tensor);
67  ARM_COMPUTE_ERROR_ON_MSG(!handle, "Couldn't create backend handle!");
68  tensor->set_handle(std::move(handle));
69  }
70  }
71 }
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
Target
< Target enum
Definition: Acl.hpp:293
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ configure_transition_manager()

void configure_transition_manager ( Graph g,
GraphContext ctx,
ExecutionWorkload workload 
)

Configures transition manager and execution workload.

Parameters
[in]gGraph to configure
[in]ctxGraph context
[in]workloadWorkload to configure

Definition at line 236 of file CrossLayerMemoryManagerHelpers.cpp.

References MemoryManagerContext::cross_group, MemoryManagerContext::cross_mm, GraphContext::memory_management_ctx(), and ExecutionWorkload::tasks.

Referenced by GraphManager::finalize_graph().

237 {
238  // Get const tensors (un-managed)
239  std::set<ITensorHandle *> const_tensors = get_const_handles(g);
240 
241  std::vector<TaskHandles> tasks_handles;
242  TargetHandleCounter target_handle_count;
243 
244  // Count handles
245  for(auto &task : workload.tasks)
246  {
247  // Populates IO handles
248  tasks_handles.push_back(get_transition_handles(ctx, task, const_tensors));
249 
250  // Count handles
251  count_input_handles_per_target(tasks_handles.back(), target_handle_count);
252  }
253 
254  // Setup memory managers
255  for(auto &hc : target_handle_count)
256  {
257  MemoryManagerContext *mm_ctx = ctx.memory_management_ctx(hc.first);
258  if(mm_ctx != nullptr)
259  {
260  if(mm_ctx->cross_mm != nullptr && mm_ctx->cross_group != nullptr)
261  {
262  // Manage and allocate tensors
263  configure_handle_lifetime(tasks_handles, hc.second);
264  }
265  }
266  }
267 }

◆ fuse_convolution_batch_normalization_with_post_ops()

void arm_compute::graph::detail::fuse_convolution_batch_normalization_with_post_ops ( Graph g,
const Edge output_edge,
unsigned int  conv_node_id,
const std::set< Activation > &  supported_fused_activations 
)

Definition at line 640 of file NodeFusionMutator.cpp.

References Graph::add_connection(), Graph::add_node(), ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, arm_compute::test::validation::conv_info, arm_compute::test::validation::data_layout, TensorDescriptor::data_type, arm_compute::test::validation::data_type, Tensor::desc(), arm_compute::graph::EltwiseLayer, arm_compute::quantization::epsilon, fuse_convolution_with_post_op(), arm_compute::graph::GEMM, get_post_op_list(), arm_compute::is_data_type_float(), TensorDescriptor::layout, NodeParams::name, arm_compute::NHWC, Graph::node(), arm_compute::test::validation::num_groups, Edge::producer(), Graph::remove_node(), INode::set_assigned_target(), TensorDescriptor::shape, Edge::tensor(), Dimensions< T >::y(), and Dimensions< T >::z().

Referenced by NodeFusionMutator::mutate().

641 {
642  ARM_COMPUTE_ERROR_ON(output_edge == nullptr);
643 
644  auto *conv_node = arm_compute::utils::cast::polymorphic_downcast<FusedConvolutionBatchNormalizationNode *>(output_edge->producer());
645  ARM_COMPUTE_ERROR_ON(conv_node->output(0) == nullptr);
646  const ConvolutionMethod conv_algorithm = conv_node->convolution_method();
647  if(conv_algorithm != ConvolutionMethod::GEMM)
648  {
649  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to non GEMM convolution\n");
650  return;
651  }
652 
653  // Prevent fusion if fused node has an output accessor
654  if(conv_node->output(0)->accessor() == nullptr)
655  {
656  // If data type is FP32/FP16, data layout is NHWC, and filter size is 1x1, fuse convolution with post op, as Conv1x1 always leads to GEMM.
657  const Edge *input_edge = conv_node->input_edge(1);
658  if(input_edge != nullptr && input_edge->tensor() != nullptr)
659  {
660  const DataLayout data_layout = input_edge->tensor()->desc().layout;
661  const DataType data_type = input_edge->tensor()->desc().data_type;
662  const TensorShape tensor_shape = input_edge->tensor()->desc().shape;
663  if((data_layout != DataLayout::NHWC) || (is_data_type_float(data_type) == false) || (tensor_shape.y() != 1) || (tensor_shape.z() != 1))
664  {
665  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to non GEMM convolution\n");
666  return;
667  }
668  }
669  else
670  {
671  return;
672  }
673 
674  // Get post op list
675  int eltwise_operand_id = 0;
676  int prev_op_dst_pos = 0; // Previous operator dst's postion in current operator
677  std::list<INode *> post_op_node_list = get_post_op_list(g, eltwise_operand_id, prev_op_dst_pos, conv_node_id, supported_fused_activations);
678 
679  if(post_op_node_list.size() == 0)
680  {
681  return;
682  }
683  else // Do convolution fusion with post op if there're one(elementwise), two or more operators
684  {
685  const Target assigned_target = conv_node->assigned_target();
686 
687  // Extract conv inputs
688  const auto conv_input_id = conv_node->input_edge(0)->producer_id();
689  const auto conv_weights_id = conv_node->input_edge(1)->producer_id();
690  const auto bn_mean_id = conv_node->input_edge(3)->producer_id();
691  const auto bn_var_id = conv_node->input_edge(4)->producer_id();
692  const auto conv_info = conv_node->convolution_info();
693  const auto conv_method = conv_node->convolution_method();
694  const auto num_groups = conv_node->num_groups();
695  FastMathHint fast_math_hint = conv_node->fast_math_hint();
696 
697  // Create the fused node
698 
699  const float epsilon = conv_node->epsilon();
700  const NodeID fused_id = g.add_node<FusedConvolutionBatchNormalizationWithPostOpsNode>(epsilon, conv_info, num_groups, conv_method, fast_math_hint);
701 
702  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing FusedConvolutionBatchNormalization node with ID : " << conv_node->id());
703 
704  // Add connections from the conv inputs to the fused node
705  g.add_connection(conv_input_id, 0, fused_id, 0);
706  g.add_connection(conv_weights_id, 0, fused_id, 1);
707 
708  if(conv_node->input_edge(2) != nullptr)
709  {
710  auto conv_bias_id = conv_node->input_edge(2)->producer_id();
711  g.add_connection(conv_bias_id, 0, fused_id, 2);
712  }
713  g.add_connection(bn_mean_id, 0, fused_id, 3);
714  g.add_connection(bn_var_id, 0, fused_id, 4);
715 
716  // Move connections of old FusedConvolutionBatchNormalization to the fused node
717  if(conv_node->input_edge(5) != nullptr)
718  {
719  const auto bn_beta_id = conv_node->input_edge(5)->producer_id();
720  g.add_connection(bn_beta_id, 0, fused_id, 5);
721  }
722 
723  if(conv_node->input_edge(6) != nullptr)
724  {
725  const auto bn_gamma_id = conv_node->input_edge(6)->producer_id();
726  g.add_connection(bn_gamma_id, 0, fused_id, 6);
727  }
728 
729  // Adding the Element wise operand in case the post op is element wise operation
730  auto it = std::find_if(post_op_node_list.begin(),
731  post_op_node_list.end(),
732  [&](const INode * nd)
733  {
734  return (nd->type() == graph::NodeType::EltwiseLayer);
735  });
736 
737  if(it != post_op_node_list.end())
738  {
739  g.add_connection(eltwise_operand_id, 0, fused_id, 7);
740  }
741 
742  // Update fused node outputs
743  auto fused_node = g.node(fused_id);
744  fused_node->set_assigned_target(assigned_target);
745 
746  auto conv_node_name = conv_node->name();
747 
748  // collect the post ops names
749  std::string post_ops_name = "";
750  for(auto &post_op : post_op_node_list)
751  {
752  post_ops_name += post_op->name();
753  }
754  fused_node->set_common_node_parameters(NodeParams{ conv_node->name() + "+" + post_ops_name, assigned_target });
755 
756  // Fuse convolution with post op
757  fuse_convolution_with_post_op(g, fused_node, post_op_node_list, prev_op_dst_pos);
758 
759  post_op_node_list.clear();
760  g.remove_node(conv_node->id());
762  }
763  }
764  else
765  {
766  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to the presence of an output accessor\n");
767  }
768 }
DataType
Definition: Acl.hpp:485
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
ConvolutionMethod
Available ConvolutionMethod.
Definition: Types.h:134
std::list< INode * > get_post_op_list(Graph &g, int &eltwise_operand_id, int &prev_op_dst_pos, unsigned int conv_node_id, const std::set< Activation > &supported_fused_activations)
const unsigned int num_groups
Definition: Im2Col.cpp:153
Target
< Target enum
Definition: Acl.hpp:293
FastMathHint
Enable or disable fast math for Convolution layer.
Definition: Types.h:143
GEMM CL kernel type.
Definition: CLTypes.h:86
unsigned int NodeID
Definition: Types.h:69
void fuse_convolution_with_post_op(Graph &g, INode *fused_node, std::list< INode *> post_op_node_list, int prev_op_dst_pos)
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50
DataLayout
[DataLayout enum definition]
Definition: Types.h:113
bool is_data_type_float(DataType dt)
Check if a given data type is of floating point type.
Definition: Utils.h:1010

◆ fuse_convolution_with_batch_normalization()

void arm_compute::graph::detail::fuse_convolution_with_batch_normalization ( Graph g,
const Edge output_edge 
)

Definition at line 82 of file NodeFusionMutator.cpp.

References Graph::add_connection(), Graph::add_node(), ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, Edge::consumer(), Edge::consumer_id(), arm_compute::test::validation::conv_info, arm_compute::quantization::epsilon, INode::input_edge(), INode::name(), NodeParams::name, Graph::node(), arm_compute::test::validation::num_groups, Edge::producer(), Edge::producer_id(), Graph::remove_node(), and transfer_driving_nodes_and_remove_old_node().

Referenced by NodeFusionMutator::mutate().

83 {
84  ARM_COMPUTE_ERROR_ON(output_edge == nullptr);
85 
86  auto *conv_node = arm_compute::utils::cast::polymorphic_downcast<ConvolutionLayerNode *>(output_edge->producer());
87  auto *bn_node = arm_compute::utils::cast::polymorphic_downcast<BatchNormalizationLayerNode *>(output_edge->consumer());
88 
89  // Not fusing if number of groups is greater than 1
90  if(conv_node->num_groups() > 1)
91  {
92  return;
93  }
94 
95  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing convolution node with ID : " << output_edge->producer_id()
96  << " with BatchNormalization Layer node with ID : " << output_edge->consumer_id() << std::endl);
97 
98  // Prevent fusion if fused node has an output accessor
99  if(conv_node->output(0)->accessor() == nullptr)
100  {
101  const Target assigned_target = conv_node->assigned_target();
102 
103  // Extract conv inputs
104  const auto conv_input_id = conv_node->input_edge(0)->producer_id();
105  const auto conv_weights_id = conv_node->input_edge(1)->producer_id();
106  const auto conv_info = conv_node->convolution_info();
107  const auto conv_method = conv_node->convolution_method();
108  const auto num_groups = conv_node->num_groups();
109  const auto act_info = bn_node->fused_activation();
110  FastMathHint fast_math_hint = conv_node->fast_math_hint();
111 
112  // Extract bn inputs
113  const auto bn_mean_id = bn_node->input_edge(1)->producer_id();
114  const auto bn_var_id = bn_node->input_edge(2)->producer_id();
115 
116  const auto epsilon = bn_node->epsilon();
117 
118  // Create the fused node
119  const NodeID fused_id = g.add_node<FusedConvolutionBatchNormalizationNode>(epsilon, conv_info, num_groups, conv_method, fast_math_hint, act_info);
120 
121  if(conv_node->input_edge(2) != nullptr)
122  {
123  auto conv_bias_id = conv_node->input_edge(2)->producer_id();
124  g.add_connection(conv_bias_id, 0, fused_id, 2);
125  }
126 
127  // Add connections from the conv/batch_norm inputs to the fused node
128  g.add_connection(conv_input_id, 0, fused_id, 0);
129  g.add_connection(conv_weights_id, 0, fused_id, 1);
130  g.add_connection(bn_mean_id, 0, fused_id, 3);
131  g.add_connection(bn_var_id, 0, fused_id, 4);
132 
133  if(bn_node->input_edge(3) != nullptr)
134  {
135  const auto bn_beta_id = bn_node->input_edge(3)->producer_id();
136  g.add_connection(bn_beta_id, 0, fused_id, 5);
137  }
138 
139  if(bn_node->input_edge(4) != nullptr)
140  {
141  const auto bn_gamma_id = bn_node->input_edge(4)->producer_id();
142  g.add_connection(bn_gamma_id, 0, fused_id, 6);
143  }
144 
145  auto fused_node = g.node(fused_id);
146  auto bn_node_name = bn_node->name();
147 
148  transfer_driving_nodes_and_remove_old_node(g, fused_node, bn_node, true);
149 
150  fused_node->set_assigned_target(assigned_target);
151  fused_node->set_common_node_parameters(NodeParams{ conv_node->name() + "+" + bn_node_name, assigned_target });
152 
153  // Remove convolution node
154  g.remove_node(conv_node->id());
155  }
156  else
157  {
158  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution with batch normalization due to the presence of an output accessor\n");
159  }
160 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
void transfer_driving_nodes_and_remove_old_node(Graph &g, INode *new_node, INode *old_node, bool add_output_tensor)
const unsigned int num_groups
Definition: Im2Col.cpp:153
Target
< Target enum
Definition: Acl.hpp:293
FastMathHint
Enable or disable fast math for Convolution layer.
Definition: Types.h:143
unsigned int NodeID
Definition: Types.h:69
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50

◆ fuse_convolution_with_post_op()

void arm_compute::graph::detail::fuse_convolution_with_post_op ( Graph g,
INode fused_node,
std::list< INode *>  post_op_node_list,
int  prev_op_dst_pos 
)

Definition at line 379 of file NodeFusionMutator.cpp.

References ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, ActivationLayerNode::node_type, EltwiseLayerNode::node_type, INode::post_op_info_list(), Graph::remove_node(), and transfer_driving_nodes_and_remove_old_node().

Referenced by fuse_convolution_batch_normalization_with_post_ops(), and fuse_convolution_with_post_ops().

380 {
381  unsigned int op_idx = 0;
382  // Fuse post operators with conv
383  for(const auto &post_op : post_op_node_list)
384  {
385  switch(post_op->type())
386  {
387  case EltwiseLayerNode::node_type:
388  {
389  auto *eltwise_node = arm_compute::utils::cast::polymorphic_downcast<EltwiseLayerNode *>(post_op);
390  ARM_COMPUTE_ERROR_ON(eltwise_node->output(0) == nullptr);
391 
392  fused_node->post_op_info_list().push_back(std::make_unique<ConvPostOpInfoEltwiseAdd>(prev_op_dst_pos, eltwise_node->convert_policy()));
393  ARM_COMPUTE_LOG_GRAPH_VERBOSE(" with Elementwise Layer node with ID : " << post_op->id());
394  break;
395  }
396  case ActivationLayerNode::node_type:
397  {
398  auto *act_node = arm_compute::utils::cast::polymorphic_downcast<ActivationLayerNode *>(post_op);
399  ARM_COMPUTE_ERROR_ON(act_node->output(0) == nullptr);
400 
401  fused_node->post_op_info_list().push_back(std::make_unique<ConvPostOpInfoActivation>(act_node->activation_info()));
402  ARM_COMPUTE_LOG_GRAPH_VERBOSE(" with Activation Layer node with ID : " << post_op->id());
403  break;
404  }
405  default:
406  {
407  break;
408  }
409  }
410 
411  if(op_idx == post_op_node_list.size() - 1) // last fusable node
412  {
413  transfer_driving_nodes_and_remove_old_node(g, fused_node, post_op, true);
414  }
415  else
416  {
417  // Remove node
418  g.remove_node(post_op->id());
419  }
420  op_idx++;
421  }
422 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
void transfer_driving_nodes_and_remove_old_node(Graph &g, INode *new_node, INode *old_node, bool add_output_tensor)
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50

◆ fuse_convolution_with_post_ops()

void arm_compute::graph::detail::fuse_convolution_with_post_ops ( Graph g,
const Edge output_edge,
unsigned int  conv_node_id,
const std::set< Activation > &  supported_fused_activations 
)

Fuse below operators:

Main operator Post operators
conv add
conv act + add
conv add + act
conv act + add + act

Notes: currently, only GEMM supports fusion with post operator

Definition at line 541 of file NodeFusionMutator.cpp.

References Graph::add_connection(), Graph::add_node(), ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, arm_compute::test::validation::conv_info, arm_compute::test::validation::data_layout, TensorDescriptor::data_type, arm_compute::test::validation::data_type, Tensor::desc(), arm_compute::graph::EltwiseLayer, fuse_convolution_with_post_op(), arm_compute::graph::GEMM, get_post_op_list(), arm_compute::is_data_type_float(), TensorDescriptor::layout, arm_compute::NHWC, Graph::node(), arm_compute::test::validation::num_groups, Edge::producer(), Graph::remove_node(), INode::set_assigned_target(), TensorDescriptor::shape, Edge::tensor(), Dimensions< T >::y(), and Dimensions< T >::z().

Referenced by NodeFusionMutator::mutate().

542 {
543  ARM_COMPUTE_ERROR_ON(output_edge == nullptr);
544 
545  auto *conv_node = arm_compute::utils::cast::polymorphic_downcast<ConvolutionLayerNode *>(output_edge->producer());
546  ARM_COMPUTE_ERROR_ON(conv_node->output(0) == nullptr);
547 
548  const ConvolutionMethod conv_algorithm = conv_node->convolution_method();
549  if(conv_algorithm != ConvolutionMethod::GEMM)
550  {
551  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to non GEMM convolution\n");
552  return;
553  }
554 
555  // Prevent fusion if fused node has an output accessor
556  if(conv_node->output(0)->accessor() == nullptr)
557  {
558  // If data type is FP32/FP16, data layout is NHWC, and filter size is 1x1, fuse convolution with post op, as Conv1x1 always leads to GEMM.
559  const Edge *input_edge = conv_node->input_edge(1);
560  if(input_edge != nullptr && input_edge->tensor() != nullptr)
561  {
562  const DataLayout data_layout = input_edge->tensor()->desc().layout;
563  const DataType data_type = input_edge->tensor()->desc().data_type;
564  const TensorShape tensor_shape = input_edge->tensor()->desc().shape;
565  if((data_layout != DataLayout::NHWC) || (is_data_type_float(data_type) == false) || (tensor_shape.y() != 1) || (tensor_shape.z() != 1))
566  {
567  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to non GEMM convolution\n");
568  return;
569  }
570  }
571  else
572  {
573  return;
574  }
575 
576  // Get post op list
577  int eltwise_operand_id = 0;
578  int prev_op_dst_pos = 0; // Previous operator dst's postion in current operator
579  std::list<INode *> post_op_node_list = get_post_op_list(g, eltwise_operand_id, prev_op_dst_pos, conv_node_id, supported_fused_activations);
580 
581  if(post_op_node_list.size() == 0)
582  {
583  return;
584  }
585  else // Do convolution fusion with post op if there're one(elementwise), two or more operators
586  {
587  const Target assigned_target = conv_node->assigned_target();
588 
589  // Extract conv inputs
590  const auto conv_input_id = conv_node->input_edge(0)->producer_id();
591  const auto conv_weights_id = conv_node->input_edge(1)->producer_id();
592  const auto conv_info = conv_node->convolution_info();
593  const auto conv_method = conv_node->convolution_method();
594  const auto num_groups = conv_node->num_groups();
595  FastMathHint fast_math_hint = conv_node->fast_math_hint();
596 
597  // Create the fused node
598  const NodeID fused_id = g.add_node<FusedConvolutionWithPostOpNode>(conv_info, num_groups, conv_method, fast_math_hint);
599  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing convolution node with ID : " << conv_node->id());
600 
601  // Add connections from the conv inputs to the fused node
602  g.add_connection(conv_input_id, 0, fused_id, 0);
603  g.add_connection(conv_weights_id, 0, fused_id, 1);
604  if(conv_node->input_edge(2) != nullptr)
605  {
606  auto conv_bias_id = conv_node->input_edge(2)->producer_id();
607  g.add_connection(conv_bias_id, 0, fused_id, 2);
608  }
609  // Adding the Element wise operand in case the post op is element wise operation
610  auto it = std::find_if(post_op_node_list.begin(),
611  post_op_node_list.end(),
612  [&](const INode * nd)
613  {
614  return (nd->type() == graph::NodeType::EltwiseLayer);
615  });
616 
617  if(it != post_op_node_list.end())
618  {
619  g.add_connection(eltwise_operand_id, 0, fused_id, 3);
620  }
621  g.remove_node(conv_node->id());
622 
623  // Update fused node outputs
624  auto fused_node = g.node(fused_id);
625  fused_node->set_assigned_target(assigned_target);
626 
627  // Fuse convolution with post op
628  fuse_convolution_with_post_op(g, fused_node, post_op_node_list, prev_op_dst_pos);
629 
630  post_op_node_list.clear();
632  }
633  }
634  else
635  {
636  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to the presence of an output accessor\n");
637  }
638 }
DataType
Definition: Acl.hpp:485
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
ConvolutionMethod
Available ConvolutionMethod.
Definition: Types.h:134
std::list< INode * > get_post_op_list(Graph &g, int &eltwise_operand_id, int &prev_op_dst_pos, unsigned int conv_node_id, const std::set< Activation > &supported_fused_activations)
const unsigned int num_groups
Definition: Im2Col.cpp:153
Target
< Target enum
Definition: Acl.hpp:293
FastMathHint
Enable or disable fast math for Convolution layer.
Definition: Types.h:143
GEMM CL kernel type.
Definition: CLTypes.h:86
unsigned int NodeID
Definition: Types.h:69
void fuse_convolution_with_post_op(Graph &g, INode *fused_node, std::list< INode *> post_op_node_list, int prev_op_dst_pos)
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50
DataLayout
[DataLayout enum definition]
Definition: Types.h:113
bool is_data_type_float(DataType dt)
Check if a given data type is of floating point type.
Definition: Utils.h:1010

◆ fuse_depthwise_convolution_with_batch_normalization()

void arm_compute::graph::detail::fuse_depthwise_convolution_with_batch_normalization ( Graph g,
const Edge output_edge 
)

Definition at line 162 of file NodeFusionMutator.cpp.

References Graph::add_connection(), Graph::add_node(), ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, Edge::consumer(), Edge::consumer_id(), arm_compute::test::validation::conv_info, arm_compute::quantization::epsilon, INode::input_edge(), INode::name(), NodeParams::name, Graph::node(), Edge::producer(), Edge::producer_id(), Graph::remove_node(), and transfer_driving_nodes_and_remove_old_node().

Referenced by NodeFusionMutator::mutate().

163 {
164  ARM_COMPUTE_ERROR_ON(output_edge == nullptr);
165 
166  auto *depth_conv_node = arm_compute::utils::cast::polymorphic_downcast<DepthwiseConvolutionLayerNode *>(output_edge->producer());
167  auto *bn_node = arm_compute::utils::cast::polymorphic_downcast<BatchNormalizationLayerNode *>(output_edge->consumer());
168 
169  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing depthwise convolution node with ID : " << output_edge->producer_id()
170  << " with BatchNormalization Layer node with ID : " << output_edge->consumer_id() << std::endl);
171 
172  // Prevent fusion if fused node has an output accessor
173  if(depth_conv_node->output(0)->accessor() == nullptr)
174  {
175  const Target assigned_target = depth_conv_node->assigned_target();
176 
177  // Extract conv inputs
178  const auto depth_conv_input_id = depth_conv_node->input_edge(0)->producer_id();
179  const auto conv_weights_id = depth_conv_node->input_edge(1)->producer_id();
180  const auto conv_info = depth_conv_node->convolution_info();
181  const auto depth_conv_method = depth_conv_node->depthwise_convolution_method();
182  const auto depth_multiplier = depth_conv_node->depth_multiplier();
183  const auto act_info = bn_node->fused_activation();
184 
185  // Extract bn inputs
186  const auto bn_mean_id = bn_node->input_edge(1)->producer_id();
187  const auto bn_var_id = bn_node->input_edge(2)->producer_id();
188  const auto bn_beta_id = bn_node->input_edge(3)->producer_id();
189  const auto bn_gamma_id = bn_node->input_edge(4)->producer_id();
190  const auto epsilon = bn_node->epsilon();
191 
192  // Create the fused node
193  const NodeID fused_id = g.add_node<FusedDepthwiseConvolutionBatchNormalizationNode>(epsilon, conv_info, depth_multiplier, depth_conv_method, act_info);
194 
195  if(depth_conv_node->input_edge(2) != nullptr)
196  {
197  const auto conv_bias_id = depth_conv_node->input_edge(2)->producer_id();
198  g.add_connection(conv_bias_id, 0, fused_id, 2);
199  }
200 
201  // Add connections from the conv/batch_norm inputs to the fused node
202  g.add_connection(depth_conv_input_id, 0, fused_id, 0);
203  g.add_connection(conv_weights_id, 0, fused_id, 1);
204  g.add_connection(bn_mean_id, 0, fused_id, 3);
205  g.add_connection(bn_var_id, 0, fused_id, 4);
206  g.add_connection(bn_beta_id, 0, fused_id, 5);
207  g.add_connection(bn_gamma_id, 0, fused_id, 6);
208 
209  auto fused_node = g.node(fused_id);
210  auto bn_node_name = bn_node->name();
211 
212  transfer_driving_nodes_and_remove_old_node(g, fused_node, bn_node, true);
213 
214  fused_node->set_assigned_target(assigned_target);
215  fused_node->set_common_node_parameters(NodeParams{ depth_conv_node->name() + "+" + bn_node_name, assigned_target });
216 
217  // Remove convolution node
218  g.remove_node(depth_conv_node->id());
219  }
220  else
221  {
222  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of depthwise convolution with batch normalization due to the presence of an output accessor\n");
223  }
224 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
void transfer_driving_nodes_and_remove_old_node(Graph &g, INode *new_node, INode *old_node, bool add_output_tensor)
Target
< Target enum
Definition: Acl.hpp:293
unsigned int NodeID
Definition: Types.h:69
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50

◆ fuse_layer() [1/2]

void arm_compute::graph::detail::fuse_layer ( Graph g,
std::function< bool(INode &)> const &  prec,
const F  fuse_fcn,
Args &&...  optional_arguments 
)

Definition at line 312 of file NodeFusionMutator.cpp.

References Graph::edge(), Graph::node(), Graph::nodes(), and INode::output_edges().

313 {
314  // Note that fused nodes may be added to the end of the node list.
315  // Instead of only looping over the original list of nodes, we loop over the current node list which could be growing.
316  // This is intentional as it probes the newly added fused nodes for further fusing opportunities.
317  for(unsigned int i = 0; i < g.nodes().size(); ++i)
318  {
319  auto node = g.node(i);
320  // Check if the node is of type N1 and not a branching node
321  if(node && node->type() == N1::node_type && node->output_edges().size() == 1)
322  {
323  const auto output_edge_id = *node->output_edges().begin();
324  const auto output_edge = g.edge(output_edge_id);
325 
326  // Check if following node is a type N2 node
327  if((output_edge != nullptr) && (output_edge->consumer() != nullptr) && (output_edge->consumer()->type() == N2::node_type) && prec(*output_edge->producer()))
328  {
329  fuse_fcn(g, output_edge, optional_arguments...);
330  }
331  }
332  }
333 }

◆ fuse_layer() [2/2]

void arm_compute::graph::detail::fuse_layer ( Graph g,
std::function< bool(INode &)> const &  prec,
const F  fuse_fcn,
Args &&...  optional_arguments 
)

Definition at line 771 of file NodeFusionMutator.cpp.

References Graph::edge(), Graph::node(), Graph::nodes(), and INode::output_edges().

772 {
773  // Note that fused nodes may be added to the end of the node list.
774  // Instead of only looping over the original list of nodes, we loop over the current node list which could be growing.
775  // This is intentional as it probes the newly added fused nodes for further fusing opportunities.
776  for(unsigned int i = 0; i < g.nodes().size(); ++i)
777  {
778  auto node = g.node(i);
779  // Check if the node is of type N1 and not a branching node
780  if(node && node->type() == N1::node_type && node->output_edges().size() == 1)
781  {
782  const auto output_edge_id = *node->output_edges().begin();
783  const auto output_edge = g.edge(output_edge_id);
784 
785  // Check if it's the correct target
786  if((output_edge != nullptr) && (output_edge->consumer() != nullptr) && prec(*output_edge->producer()))
787  {
788  fuse_fcn(g, output_edge, i, optional_arguments...);
789  }
790  }
791  }
792 }

◆ fuse_node_with_activation()

void arm_compute::graph::detail::fuse_node_with_activation ( Graph g,
const Edge output_edge,
const std::set< Activation > &  supported_fused_activations 
)

Definition at line 227 of file NodeFusionMutator.cpp.

References ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, Edge::consumer(), Edge::consumer_id(), arm_compute::graph::EltwiseLayer, arm_compute::is_data_type_float(), Edge::producer(), Edge::producer_id(), and transfer_driving_nodes_and_remove_old_node().

228 {
229  ARM_COMPUTE_ERROR_ON(output_edge == nullptr);
230 
231  auto *n_node = arm_compute::utils::cast::polymorphic_downcast<N *>(output_edge->producer());
232  auto *act_node = arm_compute::utils::cast::polymorphic_downcast<ActivationLayerNode *>(output_edge->consumer());
233 
234  ARM_COMPUTE_ERROR_ON(act_node->output(0) == nullptr || n_node->output(0) == nullptr);
235 
236  // Check if activation is supported for fusion
237  if(supported_fused_activations.count(act_node->activation_info().activation()) == 0)
238  {
239  return;
240  }
241 
242  // EltwiseLayerNode can only be fused when dataype is float
243  if(n_node->type() == NodeType::EltwiseLayer && !is_data_type_float(n_node->output(0)->desc().data_type))
244  {
245  return;
246  }
247 
248  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing node with ID : " << output_edge->producer_id()
249  << " with Activation Layer node with ID : " << output_edge->consumer_id() << std::endl);
250 
251  // Prevent fusion if fused node has an output accessor
252  if(n_node->output(0)->accessor() == nullptr)
253  {
254  // Set activation info to fused node
255  n_node->set_fused_activation(act_node->activation_info());
256 
257  transfer_driving_nodes_and_remove_old_node(g, n_node, act_node, false);
258  }
259  else
260  {
261  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of node with activation due to the presence of an output accessor\n");
262  }
263 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
void transfer_driving_nodes_and_remove_old_node(Graph &g, INode *new_node, INode *old_node, bool add_output_tensor)
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50
bool is_data_type_float(DataType dt)
Check if a given data type is of floating point type.
Definition: Utils.h:1010

◆ fuse_pad_with_convolution()

void arm_compute::graph::detail::fuse_pad_with_convolution ( Graph g,
const Edge output_edge 
)

Definition at line 266 of file NodeFusionMutator.cpp.

References Graph::add_connection(), Edge::consumer(), arm_compute::test::validation::conv_info, arm_compute::graph::get_dimension_idx(), arm_compute::graph::get_driver_nodes(), arm_compute::HEIGHT, arm_compute::graph::is_padding_in_height_or_width(), PadStrideInfo::pad_bottom(), PadStrideInfo::pad_left(), PadStrideInfo::pad_right(), PadStrideInfo::pad_top(), Edge::producer(), Graph::remove_node(), PadStrideInfo::round(), PadStrideInfo::stride(), and arm_compute::WIDTH.

267 {
268  auto *pad_node = arm_compute::utils::cast::polymorphic_downcast<PadLayerNode *>(output_edge->producer());
269  auto *conv_node = arm_compute::utils::cast::polymorphic_downcast<N *>(output_edge->consumer());
270 
271  const Edge *input_edge = pad_node->input_edge(0);
272  if(input_edge != nullptr && input_edge->tensor() != nullptr && pad_node->output(0)->accessor() == nullptr
273  && pad_node->pad_value().get<float>() == 0.0)
274  {
275  const DataLayout layout = input_edge->tensor()->desc().layout;
276  const PaddingList padding_list = pad_node->padding();
277 
278  const unsigned int height_index = get_dimension_idx(layout, DataLayoutDimension::HEIGHT);
279  const unsigned int width_index = get_dimension_idx(layout, DataLayoutDimension::WIDTH);
280 
281  const PaddingInfo pad_w = width_index < padding_list.size() ? padding_list[width_index] : PaddingInfo(0, 0);
282  const PaddingInfo pad_h = height_index < padding_list.size() ? padding_list[height_index] : PaddingInfo(0, 0);
283 
284  if(is_padding_in_height_or_width(layout, padding_list))
285  {
286  // Add paddings to the convolution node
287  const PadStrideInfo conv_info = conv_node->convolution_info();
288  const PadStrideInfo new_conv_info(
289  conv_info.stride().first,
290  conv_info.stride().second,
291  conv_info.pad_left() + pad_w.first,
292  conv_info.pad_right() + pad_w.second,
293  conv_info.pad_top() + pad_h.first,
294  conv_info.pad_bottom() + pad_h.second,
295  conv_info.round());
296  conv_node->set_convolution_info(new_conv_info);
297 
298  // Update drivers of the convolution node
299  std::vector<NodeIdxPair> pad_driver_nodes = get_driver_nodes(*pad_node);
300  g.remove_node(pad_node->id());
301 
302  // Update fused node inputs
303  for(auto &driver_node : pad_driver_nodes)
304  {
305  g.add_connection(driver_node.node_id, driver_node.index, conv_node->id(), 0);
306  }
307  }
308  }
309 }
bool is_padding_in_height_or_width(const DataLayout &layout, const PaddingList &padding_list)
Check if padding is in height and/or width dimensions.
std::vector< PaddingInfo > PaddingList
List of padding information.
Definition: Types.h:453
std::vector< NodeIdxPair > get_driver_nodes(const INode &node)
Get the list of driver nodes of a given node.
Definition: Utils.cpp:197
std::pair< uint32_t, uint32_t > PaddingInfo
Padding information as a pair of unsigned int start/end.
Definition: Types.h:450
DataLayout
[DataLayout enum definition]
Definition: Types.h:113
size_t get_dimension_idx(DataLayout data_layout, const DataLayoutDimension data_layout_dimension)
Get index of a tensor&#39;s given dimension depending on its layout.
Definition: Utils.cpp:148

◆ get_post_op_list()

std::list<INode *> arm_compute::graph::detail::get_post_op_list ( Graph g,
int &  eltwise_operand_id,
int &  prev_op_dst_pos,
unsigned int  conv_node_id,
const std::set< Activation > &  supported_fused_activations 
)

Definition at line 424 of file NodeFusionMutator.cpp.

References ARM_COMPUTE_ERROR_ON, ARM_COMPUTE_LOG_GRAPH_VERBOSE, check_post_op_type(), Edge::consumer(), arm_compute::graph::Dummy, Graph::edge(), Graph::node(), ActivationLayerNode::node_type, EltwiseLayerNode::node_type, and INode::output_edges().

Referenced by fuse_convolution_batch_normalization_with_post_ops(), and fuse_convolution_with_post_ops().

425 {
426  std::list<INode *> post_op_node_list = {};
427  NodeID prev_op_dst_id = conv_node_id;
428  NodeType post_op_type_list[3] = { NodeType::Dummy, NodeType::Dummy, NodeType::Dummy };
429  int post_op_idx = 0;
430 
431  // Get list of the connected nodes
432  auto current_node = g.node(conv_node_id);
433 
434  while(post_op_node_list.size() < 3)
435  {
436  // This convolution node must have only one output edge, otherwise this function would not have been called
437 
438  auto current_output_edge_id = current_node->output_edges().begin();
439  auto current_output_edge = g.edge(*current_output_edge_id);
440  auto post_op_node = current_output_edge->consumer();
441 
442  bool fusable_post_op = false;
443  if(post_op_node != nullptr && post_op_node->output_edges().size() > 0)
444  {
445  switch(post_op_node->type())
446  {
447  case EltwiseLayerNode::node_type:
448  {
449  auto *eltwise_node = arm_compute::utils::cast::polymorphic_downcast<EltwiseLayerNode *>(post_op_node);
450  ARM_COMPUTE_ERROR_ON(eltwise_node->output(0) == nullptr);
451  if(eltwise_node->output(0)->accessor() == nullptr)
452  {
453  post_op_node_list.push_back(post_op_node);
454  fusable_post_op = true;
455  post_op_type_list[post_op_idx++] = eltwise_node->type();
456 
457  // Extract elementwise inputs
458  const auto eltwise_input_id_0 = eltwise_node->input_edge(0)->producer_id();
459  const auto eltwise_input_id_1 = eltwise_node->input_edge(1)->producer_id();
460  if(eltwise_input_id_0 == prev_op_dst_id)
461  {
462  eltwise_operand_id = eltwise_input_id_1;
463  prev_op_dst_pos = 0;
464  }
465  else if(eltwise_input_id_1 == prev_op_dst_id)
466  {
467  eltwise_operand_id = eltwise_input_id_0;
468  prev_op_dst_pos = 1;
469  }
470  }
471  else
472  {
473  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with elementwise due to the presence of an output accessor\n");
474  }
475  break;
476  }
477  case ActivationLayerNode::node_type:
478  {
479  auto *act_node = arm_compute::utils::cast::polymorphic_downcast<ActivationLayerNode *>(post_op_node);
480  ARM_COMPUTE_ERROR_ON(act_node->output(0) == nullptr);
481  // Check if activation is supported for fusion
482  if(supported_fused_activations.count(act_node->activation_info().activation()) == 0)
483  {
484  break;
485  }
486  if(act_node->output(0)->accessor() == nullptr)
487  {
488  post_op_node_list.push_back(post_op_node);
489  fusable_post_op = true;
490  post_op_type_list[post_op_idx++] = act_node->type();
491  prev_op_dst_id = act_node->id();
492  }
493  else
494  {
495  ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented fusion of convolution node with post ops due to the presence of an output accessor\n");
496  }
497  break;
498  }
499  default:
500  {
501  break;
502  }
503  }
504 
505  // Check if the node is not a branching node and current node is fusable
506  if(post_op_node->output_edges().size() == 1 && fusable_post_op == true)
507  {
508  current_node = post_op_node;
509  }
510  else
511  {
512  break;
513  }
514  }
515  }
516 
517  // Check whether it's valid post op list
518  if(post_op_node_list.size() > 0)
519  {
520  bool fuse_with_post_op = check_post_op_type(post_op_type_list, post_op_node_list.size());
521  if(!fuse_with_post_op)
522  {
523  post_op_node_list.clear();
524  }
525  }
526 
527  return post_op_node_list;
528 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
NodeType
Supported nodes.
Definition: Types.h:199
unsigned int NodeID
Definition: Types.h:69
#define ARM_COMPUTE_LOG_GRAPH_VERBOSE(x)
Definition: Logger.h:50
bool check_post_op_type(NodeType *post_op_type, int len)

◆ prepare_all_tasks()

void prepare_all_tasks ( ExecutionWorkload workload)

Prepares all tasks for execution.

Parameters
[in]workloadWorkload to prepare

Definition at line 220 of file ExecutionHelpers.cpp.

References ARM_COMPUTE_ERROR_ON, ExecutionWorkload::graph, release_unused_tensors(), and ExecutionWorkload::tasks.

Referenced by GraphManager::finalize_graph().

221 {
222  ARM_COMPUTE_ERROR_ON(workload.graph == nullptr);
223  for(auto &task : workload.tasks)
224  {
225  task.prepare();
227  }
228 }
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
void release_unused_tensors(Graph &g)
Release the memory of all unused const nodes.

◆ release_unused_tensors()

void release_unused_tensors ( Graph g)

Release the memory of all unused const nodes.

Parameters
[in]gGraph to release the memory from

Definition at line 176 of file ExecutionHelpers.cpp.

References tensor, and Graph::tensors().

Referenced by prepare_all_tasks().

177 {
178  for(auto &tensor : g.tensors())
179  {
180  if(tensor != nullptr && tensor->handle() != nullptr)
181  {
182  tensor->handle()->release_if_unused();
183  }
184  }
185 }
CLTensor * tensor
Pointer to the auxiliary tensor.

◆ transfer_driving_nodes_and_remove_old_node()

void arm_compute::graph::detail::transfer_driving_nodes_and_remove_old_node ( Graph g,
INode new_node,
INode old_node,
bool  add_output_tensor 
)

Definition at line 48 of file NodeFusionMutator.cpp.

References Graph::add_connection(), arm_compute::graph::configure_tensor(), Tensor::extract_accessor(), arm_compute::graph::get_driving_nodes(), INode::id(), INode::output(), Graph::remove_node(), and Tensor::set_accessor().

Referenced by fuse_convolution_with_batch_normalization(), fuse_convolution_with_post_op(), fuse_depthwise_convolution_with_batch_normalization(), and fuse_node_with_activation().

49 {
50  if(new_node == nullptr || old_node == nullptr)
51  {
52  return;
53  }
54 
55  // Get driving nodes of last fusable node
56  std::vector<NodeIdxPair> last_driving_nodes = get_driving_nodes(*old_node);
57 
58  // Extract last fusable node accessor if any
59  if(old_node->output(0) == nullptr)
60  {
61  return;
62  }
63  auto old_node_accessor = old_node->output(0)->extract_accessor();
64 
65  // Remove node
66  g.remove_node(old_node->id());
67 
68  // Update fused node outputs
69  for(auto &driving_node : last_driving_nodes)
70  {
71  g.add_connection(new_node->id(), 0, driving_node.node_id, driving_node.index);
72  if(add_output_tensor)
73  {
74  configure_tensor(new_node->output(0));
75  }
76  }
77 
78  // Update accessor to fused node
79  new_node->output(0)->set_accessor(std::move(old_node_accessor));
80 }
void configure_tensor(Tensor *tensor)
Configures tensor.
Definition: Utils.cpp:217
std::vector< NodeIdxPair > get_driving_nodes(const INode &node)
Get the list of driving nodes of a given node.
Definition: Utils.cpp:177

◆ validate_all_nodes()

void validate_all_nodes ( Graph g)

Validates all nodes.

Parameters
[in]gGraph to validate

Definition at line 39 of file ExecutionHelpers.cpp.

References ARM_COMPUTE_ERROR_ON_MSG, Status::error_description(), BackendRegistry::get(), BackendRegistry::get_backend(), Graph::nodes(), and IDeviceBackend::validate_node().

Referenced by GraphManager::finalize_graph().

40 {
41  auto &nodes = g.nodes();
42 
43  // Create tasks
44  for(auto &node : nodes)
45  {
46  if(node != nullptr)
47  {
48  Target assigned_target = node->assigned_target();
49  backends::IDeviceBackend &backend = backends::BackendRegistry::get().get_backend(assigned_target);
50  Status status = backend.validate_node(*node);
51  ARM_COMPUTE_ERROR_ON_MSG(!bool(status), status.error_description().c_str());
52  }
53  }
54 }
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
Target
< Target enum
Definition: Acl.hpp:293

Variable Documentation

◆ valide_post_op_type

NodeType valide_post_op_type[4][3]
Initial value:
= { { EltwiseLayerNode::node_type },
{ EltwiseLayerNode::node_type, ActivationLayerNode::node_type },
{ ActivationLayerNode::node_type, EltwiseLayerNode::node_type },
{ ActivationLayerNode::node_type, EltwiseLayerNode::node_type, ActivationLayerNode::node_type }
}

Definition at line 347 of file NodeFusionMutator.cpp.