• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018-2020 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "arm_compute/graph/mutators/InPlaceOperationMutator.h"
25 
26 #include "arm_compute/graph/Graph.h"
27 #include "arm_compute/graph/Logger.h"
28 
29 namespace arm_compute
30 {
31 namespace graph
32 {
33 namespace
34 {
35 // Check if the output edges of the parent node are separate tensors. If not,
36 // it means the same output is connected to multiple nodes and computations on
37 // these nodes cannot be done in-place.
output_edges_are_separate_tensors(Graph & g,const Edge * input_edge)38 bool output_edges_are_separate_tensors(Graph &g, const Edge *input_edge)
39 {
40     const auto parent_node   = input_edge->producer();
41     const auto input_tensor  = input_edge->tensor();
42     const auto input_edge_id = input_edge->id();
43 
44     if(parent_node == nullptr)
45     {
46         return false;
47     }
48 
49     const auto output_edges = parent_node->output_edges();
50 
51     // If the output is connected to only one edge, then computations can
52     // be done in-place.
53     if(output_edges.size() == 1)
54     {
55         return true;
56     }
57 
58     return std::all_of(output_edges.begin(),
59                        output_edges.end(),
60                        [&](const EdgeID & edge_id)
61     {
62         // Skip check on current input edge
63         if(edge_id == input_edge_id)
64         {
65             return true;
66         }
67 
68         auto edge = g.edge(edge_id);
69         return edge->tensor() != input_tensor;
70     });
71 }
72 } // namespace
73 
name()74 const char *InPlaceOperationMutator::name()
75 {
76     return "InPlaceOperationMutator";
77 }
78 
type() const79 IGraphMutator::MutationType InPlaceOperationMutator::type() const
80 {
81     return IGraphMutator::MutationType::Backend;
82 }
83 
mutate(Graph & g)84 void InPlaceOperationMutator::mutate(Graph &g)
85 {
86     std::set<NodeType> in_place_nodes =
87     {
88         NodeType::ActivationLayer,
89         NodeType::BatchNormalizationLayer,
90         NodeType::EltwiseLayer,
91         NodeType::UnaryEltwiseLayer,
92         NodeType::PrintLayer
93     };
94 
95     // Not interested in the order of nodes
96     for(auto &node : g.nodes())
97     {
98         if(node && in_place_nodes.find(node->type()) != std::end(in_place_nodes))
99         {
100             // Get input edge
101             Edge *input_edge = node->input_edge(0);
102 
103             // Check if parent has a single output if yes then force in place calculation else not
104             if((input_edge != nullptr) && output_edges_are_separate_tensors(g, input_edge))
105             {
106                 // Get current and new output tensors
107                 auto current_output_tensor = node->output(0);
108                 auto new_output_tensor     = input_edge->tensor();
109 
110                 ARM_COMPUTE_ERROR_ON(current_output_tensor == nullptr || new_output_tensor == nullptr);
111 
112                 // Prevent in-place operation if there is an accessor bound to the in-place tensor or quantization info are different
113                 if(new_output_tensor->accessor() != nullptr || current_output_tensor->desc().quant_info != new_output_tensor->desc().quant_info)
114                 {
115                     ARM_COMPUTE_LOG_GRAPH_VERBOSE("Prevented in-place operation as there is an accessor bound to the input tensor or the quantization info are different.\n");
116                 }
117                 else
118                 {
119                     ARM_COMPUTE_LOG_GRAPH_VERBOSE("Switching to in-place computation for the node with ID : "
120                                                   << node->id() << " and name : " << node->name() << std::endl);
121                     // Update accessor
122                     new_output_tensor->set_accessor(current_output_tensor->extract_accessor());
123                     // Update output
124                     node->set_output_tensor(new_output_tensor->id(), 0);
125                 }
126             }
127         }
128     }
129 }
130 } // namespace graph
131 } // namespace arm_compute
132