blob: b6c6822c36d275c93150b5ded171a14c3ab17323 [file] [log] [blame]
Anthony Barbier2a07e182017-08-04 18:20:27 +01001/*
Gian Marco36a0a462018-01-12 10:21:40 +00002 * Copyright (c) 2017-2018 ARM Limited.
Anthony Barbier2a07e182017-08-04 18:20:27 +01003 *
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/Graph.h"
25
26#include "arm_compute/graph/CL/CLMap.h"
27#include "arm_compute/graph/CL/CLUnmap.h"
28#include "arm_compute/graph/INode.h"
Georgios Pinitase2c82fe2017-10-02 18:51:47 +010029#include "arm_compute/graph/ITensorObject.h"
Anthony Barbier2a07e182017-08-04 18:20:27 +010030#include "arm_compute/graph/Tensor.h"
Isabella Gottardib28f29d2017-11-09 17:05:07 +000031#include "arm_compute/runtime/CL/CLScheduler.h"
Anthony Barbier2a07e182017-08-04 18:20:27 +010032#include "arm_compute/runtime/CL/CLTensor.h"
33#include "arm_compute/runtime/Tensor.h"
Georgios Pinitase2c82fe2017-10-02 18:51:47 +010034#include "support/ToolchainSupport.h"
Anthony Barbier2a07e182017-08-04 18:20:27 +010035
36using namespace arm_compute::graph;
37
38struct Stage
39{
Georgios Pinitase2c82fe2017-10-02 18:51:47 +010040 ITensorObject *_input;
41 ITensorObject *_output;
Anthony Barbier2a07e182017-08-04 18:20:27 +010042 std::unique_ptr<arm_compute::IFunction> _function;
43};
44
45struct Graph::Private
46{
47public:
48 /** Finalizes the current node's configuration
49 *
50 * @param _next_hint Device execution hint
51 */
Georgios Pinitasff421f22017-10-04 16:53:58 +010052 void configure(GraphHints _next_hints);
Anthony Barbier2a07e182017-08-04 18:20:27 +010053
Georgios Pinitase2c82fe2017-10-02 18:51:47 +010054 GraphContext _ctx{};
55 std::vector<Stage> _pipeline{};
56 std::vector<std::unique_ptr<ITensorObject>> _tensors{};
57 std::vector<std::unique_ptr<INode>> _nodes{};
58 GraphHints _current_hints{};
59 GraphHints _next_hints{};
60 std::unique_ptr<ITensorObject> _graph_input{ nullptr };
61 std::unique_ptr<ITensorObject> _graph_output{ nullptr };
62 std::unique_ptr<INode> _current_node{ nullptr };
63 ITensorObject *_current_output{ nullptr };
64 bool _info_enabled{ false };
Michele Di Giorgioe3fba0a2018-02-14 14:18:01 +000065 CLTuner _tuner{};
Anthony Barbier2a07e182017-08-04 18:20:27 +010066
67private:
Georgios Pinitase2c82fe2017-10-02 18:51:47 +010068 ITensorObject *_current_input{ nullptr };
69 GraphHints _previous_hints{};
Anthony Barbier2a07e182017-08-04 18:20:27 +010070};
71
72Graph::~Graph() //NOLINT
73{
74 //Can't use =default because the destructor must be defined after Graph::Private's definition
75}
76
77Graph::Graph()
78 : _pimpl{ new Private() }
79{
Alex Gilday8913d8d2018-02-15 11:07:18 +000080 graph_init();
Michele Di Giorgioe3fba0a2018-02-14 14:18:01 +000081}
82
83void Graph::graph_init(const bool use_cl_tuner)
84{
Isabella Gottardib28f29d2017-11-09 17:05:07 +000085 // Check if OpenCL is available and initialize the scheduler
86 if(opencl_is_available())
87 {
Michele Di Giorgioe3fba0a2018-02-14 14:18:01 +000088 if(use_cl_tuner)
89 {
90 arm_compute::CLScheduler::get().default_init(&_pimpl->_tuner);
91 }
92 else
93 {
94 arm_compute::CLScheduler::get().default_init();
95 }
Isabella Gottardib28f29d2017-11-09 17:05:07 +000096 }
Anthony Barbier2a07e182017-08-04 18:20:27 +010097}
98
99void Graph::run()
100{
101 while(true)
102 {
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100103 if(_pimpl->_graph_input->has_accessor() && !_pimpl->_graph_input->call_accessor())
Anthony Barbier2a07e182017-08-04 18:20:27 +0100104 {
105 return;
106 }
107
108 for(auto &stage : _pimpl->_pipeline)
109 {
110 stage._function->run();
111 }
112
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100113 if((_pimpl->_graph_output->has_accessor() && !_pimpl->_graph_output->call_accessor())
114 || (!_pimpl->_graph_output->has_accessor()))
Anthony Barbier2a07e182017-08-04 18:20:27 +0100115 {
116 return;
117 }
118 }
119}
120
121//Finalize current node's configuration
Georgios Pinitasff421f22017-10-04 16:53:58 +0100122void Graph::Private::configure(GraphHints _next_hints)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100123{
124 ARM_COMPUTE_ERROR_ON(_current_node == nullptr);
125 ARM_COMPUTE_ERROR_ON(_graph_input == nullptr);
126
127 // Is it the first node of the graph ?
128 if(_current_input == nullptr)
129 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100130 _graph_input->set_target(_current_hints.target_hint());
131 _current_input = _graph_input.get();
132 _previous_hints = _current_hints; // For the first node just assume the previous node was of the same type as this one
Anthony Barbier2a07e182017-08-04 18:20:27 +0100133 }
134
Michele Di Giorgiodde9ec92018-02-13 15:24:04 +0000135 if(_current_node->supports_in_place())
136 {
137 _current_output = _current_input;
138 }
139
Anthony Barbier2a07e182017-08-04 18:20:27 +0100140 //Automatic output configuration ?
141 if(_current_output == nullptr)
142 {
143 _tensors.push_back(arm_compute::support::cpp14::make_unique<Tensor>(TensorInfo()));
144 _current_output = _tensors.back().get();
145 }
146
147 // If either the writer or reader node needs OpenCL then use OpenCL memory:
Georgios Pinitasff421f22017-10-04 16:53:58 +0100148 if((_next_hints.target_hint() == TargetHint::OPENCL || _current_hints.target_hint() == TargetHint::OPENCL))
Anthony Barbier2a07e182017-08-04 18:20:27 +0100149 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100150 _current_output->set_target(TargetHint::OPENCL);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100151 }
152 else
153 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100154 _current_output->set_target(TargetHint::NEON);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100155 }
156
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100157 // Instantiate Node
Georgios Pinitasff421f22017-10-04 16:53:58 +0100158 _ctx.hints() = _current_hints;
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100159 std::unique_ptr<arm_compute::IFunction> func = _current_node->instantiate_node(_ctx, _current_input, _current_output);
160
Michele Di Giorgiodde9ec92018-02-13 15:24:04 +0000161 // If the operation is done in-place, do not allocate or it will prevent following layers from performing the configuration
162 if(!_current_node->supports_in_place())
163 {
164 // Allocate current input
165 _current_input->allocate();
166 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100167
Georgios Pinitasff421f22017-10-04 16:53:58 +0100168 // Map input if needed
169 if(_current_input->target() == TargetHint::OPENCL)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100170 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100171 if(_previous_hints.target_hint() == TargetHint::NEON)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100172 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100173 ARM_COMPUTE_ERROR_ON(_current_hints.target_hint() == TargetHint::NEON);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100174 _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique<CLUnmap>(_current_input) });
175 }
Georgios Pinitasff421f22017-10-04 16:53:58 +0100176 if(_current_hints.target_hint() == TargetHint::NEON)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100177 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100178 ARM_COMPUTE_ERROR_ON(_previous_hints.target_hint() == TargetHint::NEON);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100179 _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique<CLMap>(_current_input, true) });
180 }
181 }
182
183 _pipeline.push_back({ _current_input, _current_output, std::move(func) });
184
185 _current_input = _current_output;
186 _current_output = nullptr;
Georgios Pinitasff421f22017-10-04 16:53:58 +0100187 std::swap(_previous_hints, _current_hints);
188 std::swap(_current_hints, _next_hints);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100189}
190
Anthony Barbier2a07e182017-08-04 18:20:27 +0100191void Graph::add_node(std::unique_ptr<INode> node)
192{
193 ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_input == nullptr, "The graph's input must be set before the first node is added");
194 ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_output != nullptr, "Nothing can be added after the output tensor");
195 //Trigger the creation of the current Node:
196
Georgios Pinitasff421f22017-10-04 16:53:58 +0100197 GraphHints _next_hints = _pimpl->_next_hints;
198 _next_hints.set_target_hint(node->override_target_hint(_pimpl->_next_hints.target_hint()));
199 ARM_COMPUTE_ERROR_ON(_next_hints.target_hint() == TargetHint::DONT_CARE);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100200 if(_pimpl->_current_node)
201 {
202 //Finalize the previous Node:
Georgios Pinitasff421f22017-10-04 16:53:58 +0100203 _pimpl->configure(_pimpl->_next_hints);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100204 }
205 else
206 {
Georgios Pinitasff421f22017-10-04 16:53:58 +0100207 // If that's the first node then use the same TargetHint before and after the node.
208 _pimpl->_current_hints = _next_hints;
Anthony Barbier2a07e182017-08-04 18:20:27 +0100209 }
210 if(_pimpl->_current_node)
211 {
212 _pimpl->_nodes.push_back(std::move(_pimpl->_current_node));
213 }
214 _pimpl->_current_node = std::move(node);
215}
Anthony Barbier2a07e182017-08-04 18:20:27 +0100216
217//Add a tensor with an Accessor (i.e either the input or output of the graph)
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100218void Graph::add_tensor_object(std::unique_ptr<ITensorObject> tensor)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100219{
220 // If it's the first Tensor added then it will be the input of the Graph.
221 if(_pimpl->_graph_input == nullptr)
222 {
223 ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
224 ARM_COMPUTE_ERROR_ON(_pimpl->_current_node != nullptr);
225 _pimpl->_graph_input = std::move(tensor);
226 }
227 else
228 {
229 // Else it will be the output of the Graph
230 ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
231 ARM_COMPUTE_ERROR_ON(_pimpl->_current_node == nullptr);
232 _pimpl->_graph_output = std::move(tensor);
233 _pimpl->_current_output = _pimpl->_graph_output.get();
234
235 // Finalize the graph by configuring the last Node of the graph:
Georgios Pinitasff421f22017-10-04 16:53:58 +0100236 _pimpl->configure(_pimpl->_current_hints); // Ignore _next_hint as this is the last node, and just use the same hint as before this node.
Anthony Barbier2a07e182017-08-04 18:20:27 +0100237 _pimpl->_graph_output->allocate();
238 }
239}
Gian Marco36a0a462018-01-12 10:21:40 +0000240
Isabella Gottardib28f29d2017-11-09 17:05:07 +0000241bool Graph::opencl_is_available()
242{
243 return arm_compute::opencl_is_available();
244}
Anthony Barbier2a07e182017-08-04 18:20:27 +0100245
Gian Marco36a0a462018-01-12 10:21:40 +0000246arm_compute::GPUTarget Graph::gpu_target()
247{
248 // Check if OpenCL is available before returning the GPU target
249 if(opencl_is_available())
250 {
251 return arm_compute::CLScheduler::get().target();
252 }
253 else
254 {
255 return GPUTarget::MIDGARD;
256 }
257}
258
Anthony Barbier2a07e182017-08-04 18:20:27 +0100259void Graph::set_temp(TensorInfo &&tmp)
260{
261 ARM_COMPUTE_ERROR_ON(_pimpl->_graph_input == nullptr);
262 ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
263 ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_current_output != nullptr, "TensorInfo for temporary tensor already set");
264
265 _pimpl->_tensors.push_back(arm_compute::support::cpp14::make_unique<Tensor>(std::move(tmp)));
266 _pimpl->_current_output = _pimpl->_tensors.back().get();
267}
268
Georgios Pinitasff421f22017-10-04 16:53:58 +0100269GraphHints &Graph::hints()
270{
271 return _pimpl->_next_hints;
272}
273
Anthony Barbier2a07e182017-08-04 18:20:27 +0100274Graph &arm_compute::graph::operator<<(Graph &graph, TensorInfo &&info)
275{
276 graph.set_temp(std::move(info));
277 return graph;
278}
279
280Graph &arm_compute::graph::operator<<(Graph &graph, Tensor &&tensor)
281{
Georgios Pinitase2c82fe2017-10-02 18:51:47 +0100282 graph.add_tensor_object(arm_compute::support::cpp14::make_unique<Tensor>(std::move(tensor)));
283 return graph;
284}
285
286Graph &arm_compute::graph::operator<<(Graph &graph, SubTensor &&sub_tensor)
287{
288 graph.add_tensor_object(arm_compute::support::cpp14::make_unique<SubTensor>(std::move(sub_tensor)));
Anthony Barbier2a07e182017-08-04 18:20:27 +0100289 return graph;
290}
291
Georgios Pinitasff421f22017-10-04 16:53:58 +0100292Graph &arm_compute::graph::operator<<(Graph &graph, TargetHint target_hint)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100293{
Georgios Pinitasff421f22017-10-04 16:53:58 +0100294 graph.hints().set_target_hint(target_hint);
295 return graph;
296}
297
298Graph &arm_compute::graph::operator<<(Graph &graph, ConvolutionMethodHint conv_method_hint)
299{
300 graph.hints().set_convolution_method_hint(conv_method_hint);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100301 return graph;
302}