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