From d9eb27597eabe5b7c17520f4f9b3f8a282d72573 Mon Sep 17 00:00:00 2001 From: Georgios Pinitas Date: Tue, 3 Apr 2018 13:44:29 +0100 Subject: COMPMID-797: Switch to new graph. - Cleaned up build system Change-Id: If2faa27ee5b31fa8b972836960ab3ef671059c8d Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/126435 Tested-by: Jenkins Reviewed-by: Pablo Tello --- src/graph/CL/CLMap.cpp | 43 -- src/graph/CL/CLUnmap.cpp | 43 -- src/graph/Graph.cpp | 344 +++++------- src/graph/GraphBuilder.cpp | 394 ++++++++++++++ src/graph/GraphContext.cpp | 56 +- src/graph/GraphManager.cpp | 117 ++++ src/graph/INode.cpp | 176 +++++- src/graph/NodeContext.cpp | 75 --- src/graph/OperationRegistry.cpp | 61 --- src/graph/PassManager.cpp | 88 +++ src/graph/SubGraph.cpp | 110 ---- src/graph/SubTensor.cpp | 119 ----- src/graph/Tensor.cpp | 141 ++--- src/graph/Utils.cpp | 104 ++++ src/graph/Workload.cpp | 41 ++ src/graph/backends/BackendRegistry.cpp | 63 +++ src/graph/backends/CL/CLDeviceBackend.cpp | 180 +++++++ src/graph/backends/CL/CLFunctionsFactory.cpp | 590 +++++++++++++++++++++ src/graph/backends/CL/CLNodeValidator.cpp | 64 +++ src/graph/backends/CL/CLSubTensorHandle.cpp | 78 +++ src/graph/backends/CL/CLTensorHandle.cpp | 78 +++ src/graph/backends/GLES/GCDeviceBackend.cpp | 138 +++++ src/graph/backends/GLES/GCFunctionsFactory.cpp | 507 ++++++++++++++++++ src/graph/backends/GLES/GCNodeValidator.cpp | 122 +++++ src/graph/backends/GLES/GCTensorHandle.cpp | 78 +++ src/graph/backends/NEON/NEDeviceBackend.cpp | 146 +++++ src/graph/backends/NEON/NEFunctionFactory.cpp | 563 ++++++++++++++++++++ src/graph/backends/NEON/NENodeValidator.cpp | 65 +++ src/graph/backends/NEON/NESubTensorHandle.cpp | 75 +++ src/graph/backends/NEON/NETensorHandle.cpp | 77 +++ src/graph/detail/ExecutionHelpers.cpp | 199 +++++++ src/graph/frontend/Stream.cpp | 69 +++ src/graph/frontend/SubStream.cpp | 59 +++ src/graph/mutators/DepthConcatSubTensorMutator.cpp | 86 +++ src/graph/mutators/InPlaceOperationMutator.cpp | 63 +++ src/graph/mutators/NodeFusionMutator.cpp | 96 ++++ src/graph/mutators/SplitLayerSubTensorMutator.cpp | 89 ++++ src/graph/nodes/ActivationLayer.cpp | 56 -- src/graph/nodes/ActivationLayerNode.cpp | 83 +++ src/graph/nodes/BatchNormalizationLayer.cpp | 105 ---- src/graph/nodes/BatchNormalizationLayerNode.cpp | 94 ++++ src/graph/nodes/BranchLayer.cpp | 130 ----- src/graph/nodes/ConstNode.cpp | 72 +++ src/graph/nodes/ConvolutionLayer.cpp | 363 ------------- src/graph/nodes/ConvolutionLayerNode.cpp | 111 ++++ src/graph/nodes/DeQuantizationLayer.cpp | 68 --- src/graph/nodes/DepthConcatenateLayerNode.cpp | 133 +++++ src/graph/nodes/DepthConvertLayer.cpp | 58 -- src/graph/nodes/DepthwiseConvolutionLayer.cpp | 91 ---- src/graph/nodes/DepthwiseConvolutionLayerNode.cpp | 110 ++++ src/graph/nodes/EltwiseLayerNode.cpp | 83 +++ src/graph/nodes/FlattenLayer.cpp | 54 -- src/graph/nodes/FlattenLayerNode.cpp | 80 +++ src/graph/nodes/FloorLayer.cpp | 49 -- src/graph/nodes/FullyConnectedLayer.cpp | 125 ++--- src/graph/nodes/InputNode.cpp | 72 +++ src/graph/nodes/L2NormalizeLayer.cpp | 56 -- src/graph/nodes/NormalizationLayer.cpp | 55 -- src/graph/nodes/NormalizationLayerNode.cpp | 84 +++ src/graph/nodes/OutputNode.cpp | 66 +++ src/graph/nodes/PoolingLayer.cpp | 55 -- src/graph/nodes/PoolingLayerNode.cpp | 103 ++++ src/graph/nodes/QuantizationLayer.cpp | 48 -- src/graph/nodes/ReshapeLayer.cpp | 70 ++- src/graph/nodes/ResidualLayer.cpp | 199 ------- src/graph/nodes/SoftmaxLayer.cpp | 49 -- src/graph/nodes/SoftmaxLayerNode.cpp | 84 +++ src/graph/nodes/SplitLayerNode.cpp | 117 ++++ src/graph/operations/CLSimpleOperations.cpp | 495 ----------------- src/graph/operations/NESimpleOperations.cpp | 495 ----------------- src/graph/printers/DotGraphPrinter.cpp | 173 ++++++ 71 files changed, 6167 insertions(+), 3316 deletions(-) delete mode 100644 src/graph/CL/CLMap.cpp delete mode 100644 src/graph/CL/CLUnmap.cpp create mode 100644 src/graph/GraphBuilder.cpp create mode 100644 src/graph/GraphManager.cpp delete mode 100644 src/graph/NodeContext.cpp delete mode 100644 src/graph/OperationRegistry.cpp create mode 100644 src/graph/PassManager.cpp delete mode 100644 src/graph/SubGraph.cpp delete mode 100644 src/graph/SubTensor.cpp create mode 100644 src/graph/Utils.cpp create mode 100644 src/graph/Workload.cpp create mode 100644 src/graph/backends/BackendRegistry.cpp create mode 100644 src/graph/backends/CL/CLDeviceBackend.cpp create mode 100644 src/graph/backends/CL/CLFunctionsFactory.cpp create mode 100644 src/graph/backends/CL/CLNodeValidator.cpp create mode 100644 src/graph/backends/CL/CLSubTensorHandle.cpp create mode 100644 src/graph/backends/CL/CLTensorHandle.cpp create mode 100644 src/graph/backends/GLES/GCDeviceBackend.cpp create mode 100644 src/graph/backends/GLES/GCFunctionsFactory.cpp create mode 100644 src/graph/backends/GLES/GCNodeValidator.cpp create mode 100644 src/graph/backends/GLES/GCTensorHandle.cpp create mode 100644 src/graph/backends/NEON/NEDeviceBackend.cpp create mode 100644 src/graph/backends/NEON/NEFunctionFactory.cpp create mode 100644 src/graph/backends/NEON/NENodeValidator.cpp create mode 100644 src/graph/backends/NEON/NESubTensorHandle.cpp create mode 100644 src/graph/backends/NEON/NETensorHandle.cpp create mode 100644 src/graph/detail/ExecutionHelpers.cpp create mode 100644 src/graph/frontend/Stream.cpp create mode 100644 src/graph/frontend/SubStream.cpp create mode 100644 src/graph/mutators/DepthConcatSubTensorMutator.cpp create mode 100644 src/graph/mutators/InPlaceOperationMutator.cpp create mode 100644 src/graph/mutators/NodeFusionMutator.cpp create mode 100644 src/graph/mutators/SplitLayerSubTensorMutator.cpp delete mode 100644 src/graph/nodes/ActivationLayer.cpp create mode 100644 src/graph/nodes/ActivationLayerNode.cpp delete mode 100644 src/graph/nodes/BatchNormalizationLayer.cpp create mode 100644 src/graph/nodes/BatchNormalizationLayerNode.cpp delete mode 100644 src/graph/nodes/BranchLayer.cpp create mode 100644 src/graph/nodes/ConstNode.cpp delete mode 100644 src/graph/nodes/ConvolutionLayer.cpp create mode 100644 src/graph/nodes/ConvolutionLayerNode.cpp delete mode 100644 src/graph/nodes/DeQuantizationLayer.cpp create mode 100644 src/graph/nodes/DepthConcatenateLayerNode.cpp delete mode 100644 src/graph/nodes/DepthConvertLayer.cpp delete mode 100644 src/graph/nodes/DepthwiseConvolutionLayer.cpp create mode 100644 src/graph/nodes/DepthwiseConvolutionLayerNode.cpp create mode 100644 src/graph/nodes/EltwiseLayerNode.cpp delete mode 100644 src/graph/nodes/FlattenLayer.cpp create mode 100644 src/graph/nodes/FlattenLayerNode.cpp delete mode 100644 src/graph/nodes/FloorLayer.cpp create mode 100644 src/graph/nodes/InputNode.cpp delete mode 100644 src/graph/nodes/L2NormalizeLayer.cpp delete mode 100644 src/graph/nodes/NormalizationLayer.cpp create mode 100644 src/graph/nodes/NormalizationLayerNode.cpp create mode 100644 src/graph/nodes/OutputNode.cpp delete mode 100644 src/graph/nodes/PoolingLayer.cpp create mode 100644 src/graph/nodes/PoolingLayerNode.cpp delete mode 100644 src/graph/nodes/QuantizationLayer.cpp delete mode 100644 src/graph/nodes/ResidualLayer.cpp delete mode 100644 src/graph/nodes/SoftmaxLayer.cpp create mode 100644 src/graph/nodes/SoftmaxLayerNode.cpp create mode 100644 src/graph/nodes/SplitLayerNode.cpp delete mode 100644 src/graph/operations/CLSimpleOperations.cpp delete mode 100644 src/graph/operations/NESimpleOperations.cpp create mode 100644 src/graph/printers/DotGraphPrinter.cpp (limited to 'src/graph') diff --git a/src/graph/CL/CLMap.cpp b/src/graph/CL/CLMap.cpp deleted file mode 100644 index 5289ea9a04..0000000000 --- a/src/graph/CL/CLMap.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/CL/CLMap.h" - -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/Helpers.h" -#include "arm_compute/core/Validate.h" -#include "arm_compute/graph/ITensorObject.h" -#include "arm_compute/runtime/CL/CLScheduler.h" - -using namespace arm_compute::graph; - -CLMap::CLMap(ITensorObject *tensor, bool blocking) - : _tensor(dynamic_cast(tensor->tensor())), _blocking(blocking) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor); -} - -void CLMap::run() -{ - _tensor->map(arm_compute::CLScheduler::get().queue(), _blocking); -} diff --git a/src/graph/CL/CLUnmap.cpp b/src/graph/CL/CLUnmap.cpp deleted file mode 100644 index 31f2f19e9c..0000000000 --- a/src/graph/CL/CLUnmap.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/CL/CLUnmap.h" - -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/Helpers.h" -#include "arm_compute/core/Validate.h" -#include "arm_compute/graph/ITensorObject.h" -#include "arm_compute/runtime/CL/CLScheduler.h" - -using namespace arm_compute::graph; - -CLUnmap::CLUnmap(ITensorObject *tensor) - : _tensor(dynamic_cast(tensor->tensor())) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor); -} - -void CLUnmap::run() -{ - _tensor->unmap(arm_compute::CLScheduler::get().queue()); -} diff --git a/src/graph/Graph.cpp b/src/graph/Graph.cpp index 47bd672114..e1ffeed668 100644 --- a/src/graph/Graph.cpp +++ b/src/graph/Graph.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -23,293 +23,205 @@ */ #include "arm_compute/graph/Graph.h" -#include "arm_compute/graph/CL/CLMap.h" -#include "arm_compute/graph/CL/CLUnmap.h" -#include "arm_compute/graph/INode.h" -#include "arm_compute/graph/ITensorObject.h" -#include "arm_compute/graph/Tensor.h" -#include "arm_compute/runtime/CL/CLScheduler.h" -#include "arm_compute/runtime/CL/CLTensor.h" -#include "arm_compute/runtime/CL/CLTuner.h" -#include "arm_compute/runtime/Tensor.h" -#include "support/ToolchainSupport.h" - -#include - -using namespace arm_compute::graph; - -namespace +namespace arm_compute { -bool file_exists(const std::string &filename) +namespace graph { - std::ifstream file(filename); - return file.good(); -} - -} // namespace -struct Stage +Graph::Graph(GraphID id, std::string name) + : _id(id), _name(std::move(name)), _nodes(), _edges(), _tensors(), _tagged_nodes(), _mtx() { - ITensorObject *_input; - ITensorObject *_output; - std::unique_ptr _function; -}; +} -struct Graph::Private -{ -public: - /** Finalizes the current node's configuration - * - * @param _next_hint Device execution hint - */ - void configure(GraphHints _next_hints); - - GraphContext _ctx{}; - std::vector _pipeline{}; - std::vector> _tensors{}; - std::vector> _nodes{}; - GraphHints _current_hints{}; - GraphHints _next_hints{}; - std::unique_ptr _graph_input{ nullptr }; - std::unique_ptr _graph_output{ nullptr }; - std::unique_ptr _current_node{ nullptr }; - ITensorObject *_current_output{ nullptr }; - bool _info_enabled{ false }; - CLTuner _tuner{}; - -private: - ITensorObject *_current_input{ nullptr }; - GraphHints _previous_hints{}; -}; - -static const std::string tuner_data_filename = "acl_tuner.csv"; -Graph::~Graph() //NOLINT +bool Graph::remove_node(NodeID nid) { - if(_pimpl->_tuner.tune_new_kernels() && !_pimpl->_tuner.lws_table().empty()) + if(nid >= _nodes.size()) { - _pimpl->_tuner.save_to_file(tuner_data_filename); + return false; } -} -Graph::Graph() - : _pimpl{ new Private() } -{ - graph_init(); -} + std::unique_ptr &node = _nodes[nid]; -void Graph::graph_init(const bool use_cl_tuner) -{ - // Check if OpenCL is available and initialize the scheduler - if(opencl_is_available()) + // Remove node connections + if(node) { - if(_pimpl->_tuner.lws_table().empty() && file_exists(tuner_data_filename)) + for(auto &input_eid : node->_input_edges) { - _pimpl->_tuner.load_from_file(tuner_data_filename); + remove_connection(input_eid); } - _pimpl->_tuner.set_tune_new_kernels(use_cl_tuner); - arm_compute::CLScheduler::get().default_init(&_pimpl->_tuner); - } -} -void Graph::run() -{ - while(true) - { - if(_pimpl->_graph_input->has_accessor() && !_pimpl->_graph_input->call_accessor()) + for(auto &outpud_eid : node->_output_edges) { - return; + remove_connection(outpud_eid); } + } - for(auto &stage : _pimpl->_pipeline) - { - stage._function->run(); - } + node = nullptr; - if((_pimpl->_graph_output->has_accessor() && !_pimpl->_graph_output->call_accessor()) - || (!_pimpl->_graph_output->has_accessor())) - { - return; - } - } + return true; } -//Finalize current node's configuration -void Graph::Private::configure(GraphHints _next_hints) +EdgeID Graph::add_connection(NodeID source, size_t source_idx, NodeID sink, size_t sink_idx) { - ARM_COMPUTE_ERROR_ON(_current_node == nullptr); - ARM_COMPUTE_ERROR_ON(_graph_input == nullptr); + std::lock_guard lock(_mtx); - // Is it the first node of the graph ? - if(_current_input == nullptr) - { - _graph_input->set_target(_current_hints.target_hint()); - _current_input = _graph_input.get(); - _previous_hints = _current_hints; // For the first node just assume the previous node was of the same type as this one - } + // Check if node index is valid, if node exists and finally if the connection index is valid + ARM_COMPUTE_ERROR_ON((source >= _nodes.size()) || (_nodes[source] == nullptr) || (source_idx >= _nodes[source]->num_outputs())); + ARM_COMPUTE_ERROR_ON((sink >= _nodes.size()) || (_nodes[sink] == nullptr) || (sink_idx >= _nodes[sink]->num_inputs())); - if(_current_node->supports_in_place()) - { - _current_output = _current_input; - } + // Get nodes + std::unique_ptr &source_node = _nodes[source]; + std::unique_ptr &sink_node = _nodes[sink]; - //Automatic output configuration ? - if(_current_output == nullptr) + // Check for duplicate connections (Check only sink node) + Edge *sink_node_edge = sink_node->input_edge(sink_idx); + if((sink_node_edge != nullptr) && (sink_node_edge->producer_id() == source) && (sink_node_edge->producer_idx() == source_idx) + && (sink_node_edge->consumer_id() == sink) && (sink_node_edge->consumer_idx() == sink_idx)) { - _tensors.push_back(arm_compute::support::cpp14::make_unique(TensorInfo())); - _current_output = _tensors.back().get(); + return sink_node_edge->id(); } - // If either the writer or reader node needs OpenCL then use OpenCL memory: - if((_next_hints.target_hint() == TargetHint::OPENCL || _current_hints.target_hint() == TargetHint::OPENCL)) + // Check if there is already a tensor associated with output if not create one + TensorID tid = source_node->output_id(source_idx); + if(tid == NullTensorID) { - _current_output->set_target(TargetHint::OPENCL); - } - else - { - _current_output->set_target(TargetHint::NEON); + tid = create_tensor(); } + std::unique_ptr &tensor = _tensors[tid]; + + // Create connections + EdgeID eid = _edges.size(); + auto connection = arm_compute::support::cpp14::make_unique(eid, source_node.get(), source_idx, sink_node.get(), sink_idx, tensor.get()); + _edges.push_back(std::move(connection)); + + // Add connections to source and sink nodes + source_node->_output_edges.insert(eid); + sink_node->_input_edges[sink_idx] = eid; - // Instantiate Node - _ctx.hints() = _current_hints; - std::unique_ptr func = _current_node->instantiate_node(_ctx, _current_input, _current_output); + // Set tensor output node + source_node->_outputs[source_idx] = tid; + + // Bind tensor to the edge + tensor->bind_edge(eid); + + // Try and propagate shapes in sink node + sink_node->forward_descriptors(); + + return eid; +} - // If the operation is done in-place, do not allocate or it will prevent following layers from performing the configuration - if(!_current_node->supports_in_place()) +bool Graph::remove_connection(EdgeID eid) +{ + if(eid >= _edges.size()) { - // Allocate current input - _current_input->allocate(); + return false; } - // Map input if needed - if(_current_input->target() == TargetHint::OPENCL) + std::unique_ptr &edge = _edges[eid]; + + // Remove node connections + if(edge != nullptr) { - if(_previous_hints.target_hint() == TargetHint::NEON) + // Get tensor bound to the edge + if(edge->tensor() != nullptr) + { + edge->tensor()->unbind_edge(eid); + } + + // Remove edges from source node + if(edge->producer() != nullptr) { - ARM_COMPUTE_ERROR_ON(_current_hints.target_hint() == TargetHint::NEON); - _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique(_current_input) }); + edge->producer()->_output_edges.erase(eid); } - if(_current_hints.target_hint() == TargetHint::NEON) + + // Remove edges from sink node + if((edge->consumer() != nullptr) && (edge->consumer_idx() < edge->consumer()->_input_edges.size())) { - ARM_COMPUTE_ERROR_ON(_previous_hints.target_hint() == TargetHint::NEON); - _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique(_current_input, true) }); + edge->consumer()->_input_edges[edge->consumer_idx()] = EmptyEdgeID; } } - _pipeline.push_back({ _current_input, _current_output, std::move(func) }); + // Clear edge + edge = nullptr; - _current_input = _current_output; - _current_output = nullptr; - std::swap(_previous_hints, _current_hints); - std::swap(_current_hints, _next_hints); + return true; } -void Graph::add_node(std::unique_ptr node) +TensorID Graph::create_tensor(TensorDescriptor desc) { - ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_input == nullptr, "The graph's input must be set before the first node is added"); - ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_output != nullptr, "Nothing can be added after the output tensor"); - //Trigger the creation of the current Node: - - GraphHints _next_hints = _pimpl->_next_hints; - _next_hints.set_target_hint(node->override_target_hint(_pimpl->_next_hints.target_hint())); - ARM_COMPUTE_ERROR_ON(_next_hints.target_hint() == TargetHint::DONT_CARE); - if(_pimpl->_current_node) - { - //Finalize the previous Node: - _pimpl->configure(_pimpl->_next_hints); - } - else - { - // If that's the first node then use the same TargetHint before and after the node. - _pimpl->_current_hints = _next_hints; - } - if(_pimpl->_current_node) - { - _pimpl->_nodes.push_back(std::move(_pimpl->_current_node)); - } - _pimpl->_current_node = std::move(node); + TensorID tid = _tensors.size(); + auto tensor = support::cpp14::make_unique(tid, desc); + _tensors.push_back(std::move(tensor)); + + return tid; } -//Add a tensor with an Accessor (i.e either the input or output of the graph) -void Graph::add_tensor_object(std::unique_ptr tensor) +std::string Graph::name() const { - // If it's the first Tensor added then it will be the input of the Graph. - if(_pimpl->_graph_input == nullptr) - { - ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr); - ARM_COMPUTE_ERROR_ON(_pimpl->_current_node != nullptr); - _pimpl->_graph_input = std::move(tensor); - } - else - { - // Else it will be the output of the Graph - ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr); - ARM_COMPUTE_ERROR_ON(_pimpl->_current_node == nullptr); - _pimpl->_graph_output = std::move(tensor); - _pimpl->_current_output = _pimpl->_graph_output.get(); - - // Finalize the graph by configuring the last Node of the graph: - _pimpl->configure(_pimpl->_current_hints); // Ignore _next_hint as this is the last node, and just use the same hint as before this node. - _pimpl->_graph_output->allocate(); - } + return _name; } -bool Graph::opencl_is_available() +GraphID Graph::id() const { - return arm_compute::opencl_is_available(); + return _id; } -arm_compute::GPUTarget Graph::gpu_target() +const std::vector &Graph::inputs() { - // Check if OpenCL is available before returning the GPU target - if(opencl_is_available()) - { - return arm_compute::CLScheduler::get().target(); - } - else - { - return GPUTarget::MIDGARD; - } + return _tagged_nodes[NodeType::Input]; +} + +std::vector> &Graph::nodes() +{ + return _nodes; } -void Graph::set_temp(TensorInfo &&tmp) +const std::vector> &Graph::nodes() const { - ARM_COMPUTE_ERROR_ON(_pimpl->_graph_input == nullptr); - ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr); - ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_current_output != nullptr, "TensorInfo for temporary tensor already set"); + return _nodes; +} + +const std::vector> &Graph::edges() const +{ + return _edges; +} - _pimpl->_tensors.push_back(arm_compute::support::cpp14::make_unique(std::move(tmp))); - _pimpl->_current_output = _pimpl->_tensors.back().get(); +std::vector> &Graph::tensors() +{ + return _tensors; +} + +const std::vector> &Graph::tensors() const +{ + return _tensors; } -GraphHints &Graph::hints() +const INode *Graph::node(NodeID id) const { - return _pimpl->_next_hints; + return (id >= _nodes.size()) ? nullptr : _nodes[id].get(); } -Graph &arm_compute::graph::operator<<(Graph &graph, TensorInfo &&info) +INode *Graph::node(NodeID id) { - graph.set_temp(std::move(info)); - return graph; + return (id >= _nodes.size()) ? nullptr : _nodes[id].get(); } -Graph &arm_compute::graph::operator<<(Graph &graph, Tensor &&tensor) +const Edge *Graph::edge(EdgeID id) const { - graph.add_tensor_object(arm_compute::support::cpp14::make_unique(std::move(tensor))); - return graph; + return (id >= _edges.size()) ? nullptr : _edges[id].get(); } -Graph &arm_compute::graph::operator<<(Graph &graph, SubTensor &&sub_tensor) +Edge *Graph::edge(EdgeID id) { - graph.add_tensor_object(arm_compute::support::cpp14::make_unique(std::move(sub_tensor))); - return graph; + return (id >= _edges.size()) ? nullptr : _edges[id].get(); } -Graph &arm_compute::graph::operator<<(Graph &graph, TargetHint target_hint) +const Tensor *Graph::tensor(TensorID id) const { - graph.hints().set_target_hint(target_hint); - return graph; + return (id >= _tensors.size()) ? nullptr : _tensors[id].get(); } -Graph &arm_compute::graph::operator<<(Graph &graph, ConvolutionMethodHint conv_method_hint) +Tensor *Graph::tensor(TensorID id) { - graph.hints().set_convolution_method_hint(conv_method_hint); - return graph; + return (id >= _tensors.size()) ? nullptr : _tensors[id].get(); } +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/GraphBuilder.cpp b/src/graph/GraphBuilder.cpp new file mode 100644 index 0000000000..0d1bdc3596 --- /dev/null +++ b/src/graph/GraphBuilder.cpp @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/GraphBuilder.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Utils.h" +#include "arm_compute/graph/algorithms/BFS.h" +#include "arm_compute/graph/nodes/Nodes.h" + +#define CHECK_NODEIDX_PAIR(pair, g) \ + ARM_COMPUTE_ERROR_ON(((pair).node_id >= (g).nodes().size()) || ((g).node((pair).node_id) == nullptr) || ((pair).index >= (g).node((pair).node_id)->num_outputs())); + +namespace arm_compute +{ +namespace graph +{ +namespace +{ +Status set_node_params(Graph &g, NodeID nid, NodeParams ¶ms) +{ + INode *node = g.node(nid); + ARM_COMPUTE_RETURN_ERROR_ON(!node); + + node->set_common_node_parameters(params); + + return Status{}; +} + +Status set_accessor_on_node(Graph &g, NodeID nid, bool is_output, size_t idx, ITensorAccessorUPtr accessor) +{ + INode *node = g.node(nid); + ARM_COMPUTE_RETURN_ERROR_ON(!node); + + Tensor *tensor = is_output ? node->output(idx) : node->input(idx); + ARM_COMPUTE_RETURN_ERROR_ON(!tensor); + + tensor->set_accessor(std::move(accessor)); + + return Status{}; +} + +NodeID add_const_node_with_name(Graph &g, NodeParams params, const std::string &name, TensorDescriptor desc, ITensorAccessorUPtr accessor) +{ + params.name = params.name.empty() ? "" : params.name + name; + auto nid = GraphBuilder::add_const_node(g, params, desc, std::move(accessor)); + set_node_params(g, nid, params); + return nid; +} + +template +NodeID create_simple_single_input_output_node(Graph &g, NodeParams ¶ms, NodeIdxPair input, Args &&... args) +{ + CHECK_NODEIDX_PAIR(input, g); + + NodeID nid = g.add_node(std::forward(args)...); + g.add_connection(input.node_id, input.index, nid, 0); + set_node_params(g, nid, params); + + return nid; +} + +NodeID create_grouped_convolution(Graph &g, NodeParams ¶ms, NodeIdxPair input, NodeID weights, NodeID bias, + PadStrideInfo conv_info, ConvolutionMethod method, unsigned int num_groups) +{ + bool has_bias = (bias != EmptyNodeID); + + // Split input + NodeID input_split = GraphBuilder::add_split_node(g, params, input, num_groups, 2); + + // Split weights + NodeID weights_split = GraphBuilder::add_split_node(g, params, { weights, 0 }, num_groups, 3); + + // Split bias + NodeID bias_split = EmptyNodeID; + if(has_bias) + { + // Split bias + bias_split = GraphBuilder::add_split_node(g, params, { bias, 0 }, num_groups, 0); + } + + std::vector convolution_outputs; + for(unsigned int i = 0; i < num_groups; ++i) + { + NodeID conv_nid = g.add_node(conv_info, method); + g.add_connection(input_split, i, conv_nid, 0); + g.add_connection(weights_split, i, conv_nid, 1); + if(has_bias) + { + g.add_connection(bias_split, i, conv_nid, 2); + } + set_node_params(g, conv_nid, params); + convolution_outputs.push_back({ conv_nid, 0 }); + } + + // Depth concatenate output + return GraphBuilder::add_depth_concatenate_node(g, params, convolution_outputs); +} +} // namespace + +NodeID GraphBuilder::add_const_node(Graph &g, NodeParams params, TensorDescriptor desc, ITensorAccessorUPtr accessor) +{ + auto nid = g.add_node(desc); + set_node_params(g, nid, params); + set_accessor_on_node(g, nid, true, 0, std::move(accessor)); + return nid; +} + +NodeID GraphBuilder::add_input_node(Graph &g, NodeParams params, TensorDescriptor desc, ITensorAccessorUPtr accessor) +{ + auto nid = g.add_node(desc); + set_node_params(g, nid, params); + set_accessor_on_node(g, nid, true, 0, std::move(accessor)); + return nid; +} + +NodeID GraphBuilder::add_output_node(Graph &g, NodeParams params, NodeIdxPair input, ITensorAccessorUPtr accessor) +{ + CHECK_NODEIDX_PAIR(input, g); + + NodeID nid = g.add_node(); + g.add_connection(input.node_id, input.index, nid, 0); + set_node_params(g, nid, params); + set_accessor_on_node(g, nid, false, 0, std::move(accessor)); + + return nid; +} + +NodeID GraphBuilder::add_activation_node(Graph &g, NodeParams params, NodeIdxPair input, ActivationLayerInfo act_info) +{ + return create_simple_single_input_output_node(g, params, input, act_info); +} + +NodeID GraphBuilder::add_batch_normalization_node(Graph &g, NodeParams params, NodeIdxPair input, float epsilon, + ITensorAccessorUPtr mean_accessor, ITensorAccessorUPtr var_accessor, + ITensorAccessorUPtr beta_accessor, ITensorAccessorUPtr gamma_accessor) +{ + CHECK_NODEIDX_PAIR(input, g); + + bool has_beta = (beta_accessor != nullptr); + bool has_gamma = (gamma_accessor != nullptr); + + // Get input tensor descriptor + const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]); + + // Calculate Common Descriptor + TensorDescriptor common_desc = input_tensor_desc; + common_desc.shape = TensorShape(common_desc.shape.z()); + + // Create mean and nodes + auto mean_nid = add_const_node_with_name(g, params, "Mean", common_desc, std::move(mean_accessor)); + auto var_nid = add_const_node_with_name(g, params, "Variance", common_desc, std::move(var_accessor)); + + // Create beta node + NodeID beta_nid = EmptyNodeID; + if(has_beta) + { + beta_nid = add_const_node_with_name(g, params, "Beta", common_desc, std::move(beta_accessor)); + } + + // Create gamma node + NodeID gamma_nid = EmptyNodeID; + if(has_gamma) + { + gamma_nid = add_const_node_with_name(g, params, "Gamma", common_desc, std::move(gamma_accessor)); + } + + // Create batch normalization node and add connections + NodeID batch_norm_nid = g.add_node(epsilon); + g.add_connection(input.node_id, input.index, batch_norm_nid, 0); + g.add_connection(mean_nid, 0, batch_norm_nid, 1); + g.add_connection(var_nid, 0, batch_norm_nid, 2); + if(has_beta) + { + g.add_connection(beta_nid, 0, batch_norm_nid, 3); + } + if(has_gamma) + { + g.add_connection(gamma_nid, 0, batch_norm_nid, 4); + } + set_node_params(g, batch_norm_nid, params); + + return batch_norm_nid; +} + +NodeID GraphBuilder::add_convolution_node(Graph &g, NodeParams params, NodeIdxPair input, + Size2D kernel_spatial_extend, unsigned int depth, PadStrideInfo conv_info, + unsigned int num_groups, ConvolutionMethod method, + ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor) +{ + CHECK_NODEIDX_PAIR(input, g); + ARM_COMPUTE_ERROR_ON(depth == 0); + ARM_COMPUTE_ERROR_ON((kernel_spatial_extend.width == 0) || (kernel_spatial_extend.height == 0)); + + bool has_bias = (bias_accessor != nullptr); + + // Get input tensor descriptor + const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]); + + // Create weights node + TensorDescriptor w_desc = input_tensor_desc; + w_desc.shape = TensorShape(kernel_spatial_extend.width, kernel_spatial_extend.height, w_desc.shape.z() / num_groups, depth); + NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor)); + + // Create bias nodes + NodeID b_nid = EmptyNodeID; + if(has_bias) + { + TensorDescriptor b_desc = input_tensor_desc; + b_desc.shape = TensorShape(depth); + b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor)); + } + + if(num_groups == 1) + { + // Create convolution node and connect + NodeID conv_nid = g.add_node(conv_info, method); + g.add_connection(input.node_id, input.index, conv_nid, 0); + g.add_connection(w_nid, 0, conv_nid, 1); + if(has_bias) + { + g.add_connection(b_nid, 0, conv_nid, 2); + } + set_node_params(g, conv_nid, params); + + return conv_nid; + } + else + { + return create_grouped_convolution(g, params, input, w_nid, b_nid, conv_info, method, num_groups); + } +} + +NodeID GraphBuilder::add_depth_concatenate_node(Graph &g, NodeParams params, std::vector inputs) +{ + ARM_COMPUTE_ERROR_ON(inputs.size() == 0); + + NodeID nid = g.add_node(inputs.size()); + + unsigned int i = 0; + for(const auto &input : inputs) + { + CHECK_NODEIDX_PAIR(input, g); + g.add_connection(input.node_id, input.index, nid, i++); + } + set_node_params(g, nid, params); + + return nid; +} + +NodeID GraphBuilder::add_depthwise_convolution_node(Graph &g, NodeParams params, NodeIdxPair input, Size2D kernel_spatial_extend, PadStrideInfo conv_info, + DepthwiseConvolutionMethod method, + ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor) +{ + CHECK_NODEIDX_PAIR(input, g); + ARM_COMPUTE_ERROR_ON((kernel_spatial_extend.width == 0) || (kernel_spatial_extend.height == 0)); + + bool has_bias = (bias_accessor != nullptr); + + // Get input tensor descriptor + const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]); + + // Create weights node + TensorDescriptor w_desc = input_tensor_desc; + w_desc.shape = TensorShape(kernel_spatial_extend.width, kernel_spatial_extend.height, w_desc.shape.z()); + NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor)); + + // Create bias nodes + NodeID b_nid = EmptyNodeID; + if(has_bias) + { + TensorDescriptor b_desc = input_tensor_desc; + b_desc.shape = TensorShape(b_desc.shape.z()); + b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor)); + } + + // Create convolution node and connect + NodeID conv_nid = g.add_node(conv_info, method); + g.add_connection(input.node_id, input.index, conv_nid, 0); + g.add_connection(w_nid, 0, conv_nid, 1); + if(has_bias) + { + g.add_connection(b_nid, 0, conv_nid, 2); + } + set_node_params(g, conv_nid, params); + + return conv_nid; +} + +NodeID GraphBuilder::add_elementwise_node(Graph &g, NodeParams params, NodeIdxPair input0, NodeIdxPair input1, EltwiseOperation operation) +{ + CHECK_NODEIDX_PAIR(input0, g); + CHECK_NODEIDX_PAIR(input1, g); + + NodeID nid = g.add_node(operation); + + g.add_connection(input0.node_id, input0.index, nid, 0); + g.add_connection(input1.node_id, input1.index, nid, 1); + + set_node_params(g, nid, params); + + return nid; +} + +NodeID GraphBuilder::add_flatten_node(Graph &g, NodeParams params, NodeIdxPair input) +{ + return create_simple_single_input_output_node(g, params, input); +} + +NodeID GraphBuilder::add_fully_connected_layer(Graph &g, NodeParams params, NodeIdxPair input, unsigned int num_outputs, + ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor) +{ + CHECK_NODEIDX_PAIR(input, g); + ARM_COMPUTE_ERROR_ON(num_outputs == 0); + + bool has_bias = (bias_accessor != nullptr); + + // Get input tensor descriptor + const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]); + + // Create weights node + TensorDescriptor w_desc = input_tensor_desc; + w_desc.shape = FullyConnectedLayerNode::compute_weights_shape(input_tensor_desc.shape, num_outputs); + NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor)); + + // Create bias nodes + NodeID b_nid = EmptyNodeID; + if(has_bias) + { + TensorDescriptor b_desc = input_tensor_desc; + b_desc.shape = TensorShape(num_outputs); + b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor)); + } + + // Create convolution node and connect + NodeID fc_nid = g.add_node(num_outputs); + g.add_connection(input.node_id, input.index, fc_nid, 0); + g.add_connection(w_nid, 0, fc_nid, 1); + if(has_bias) + { + g.add_connection(b_nid, 0, fc_nid, 2); + } + + set_node_params(g, fc_nid, params); + + return fc_nid; +} + +NodeID GraphBuilder::add_normalization_node(Graph &g, NodeParams params, NodeIdxPair input, NormalizationLayerInfo norm_info) +{ + return create_simple_single_input_output_node(g, params, input, norm_info); +} + +NodeID GraphBuilder::add_pooling_node(Graph &g, NodeParams params, NodeIdxPair input, PoolingLayerInfo pool_info) +{ + return create_simple_single_input_output_node(g, params, input, pool_info); +} + +NodeID GraphBuilder::add_reshape_node(Graph &g, NodeParams params, NodeIdxPair input, TensorShape shape) +{ + return create_simple_single_input_output_node(g, params, input, shape); +} + +NodeID GraphBuilder::add_softmax_node(Graph &g, NodeParams params, NodeIdxPair input, float beta) +{ + return create_simple_single_input_output_node(g, params, input, beta); +} + +NodeID GraphBuilder::add_split_node(Graph &g, NodeParams params, NodeIdxPair input, unsigned int num_splits, unsigned int axis) +{ + return create_simple_single_input_output_node(g, params, input, num_splits, axis); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/GraphContext.cpp b/src/graph/GraphContext.cpp index bfc6fcdfca..6fc45c0aa7 100644 --- a/src/graph/GraphContext.cpp +++ b/src/graph/GraphContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -22,45 +22,53 @@ * SOFTWARE. */ #include "arm_compute/graph/GraphContext.h" +#include -using namespace arm_compute::graph; - -GraphHints::GraphHints(TargetHint target_hint, ConvolutionMethodHint conv_method_hint) - : _target_hint(target_hint), _convolution_method_hint(conv_method_hint) +namespace arm_compute { -} - -void GraphHints::set_target_hint(TargetHint target_hint) +namespace graph { - _target_hint = target_hint; -} - -void GraphHints::set_convolution_method_hint(ConvolutionMethodHint convolution_method) +GraphContext::GraphContext() + : _config(), _memory_managers() { - _convolution_method_hint = convolution_method; } -TargetHint GraphHints::target_hint() const +const GraphConfig &GraphContext::config() const { - return _target_hint; + return _config; } -ConvolutionMethodHint GraphHints::convolution_method_hint() const +void GraphContext::set_config(const GraphConfig &config) { - return _convolution_method_hint; + _config = config; } -GraphContext::GraphContext() - : _hints() +bool GraphContext::insert_memory_management_ctx(MemoryManagerContext &&memory_ctx) { + Target target = memory_ctx.target; + if(target == Target::UNSPECIFIED || _memory_managers.find(target) != std::end(_memory_managers)) + { + return false; + } + + _memory_managers[target] = std::move(memory_ctx); + return true; } -GraphHints &GraphContext::hints() +MemoryManagerContext *GraphContext::memory_management_ctx(Target target) { - return _hints; + return (_memory_managers.find(target) != std::end(_memory_managers)) ? &_memory_managers[target] : nullptr; } -const GraphHints &GraphContext::hints() const +void GraphContext::finalize() { - return _hints; -} \ No newline at end of file + for(auto &mm_obj : _memory_managers) + { + if(mm_obj.second.mm != nullptr) + { + mm_obj.second.mm->finalize(); + } + } +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/GraphManager.cpp b/src/graph/GraphManager.cpp new file mode 100644 index 0000000000..759300e0c9 --- /dev/null +++ b/src/graph/GraphManager.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/GraphManager.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/PassManager.h" +#include "arm_compute/graph/Utils.h" +#include "arm_compute/graph/detail/ExecutionHelpers.h" + +namespace arm_compute +{ +namespace graph +{ +GraphManager::GraphManager() + : _workloads() +{ + detail::default_initialize_backends(); +} + +void GraphManager::finalize_graph(Graph &graph, GraphContext &ctx, PassManager &pm, Target target) +{ + // Setup graph context if not done manually + setup_default_graph_context(ctx); + + // Check if graph has been registered + ARM_COMPUTE_ERROR_ON_MSG(_workloads.find(graph.id()) != std::end(_workloads), "Graph is already registered!"); + + // Force target to all graph construct + // TODO (geopin01) : Support heterogeneous execution + Target forced_target = is_target_supported(target) ? target : get_default_target(); + force_target_to_graph(graph, forced_target); + + // Configure all tensors + detail::configure_all_tensors(graph); + + // Apply all mutating passes + pm.run_all(graph); + + // TODO (geopin01): Perform a graph validation + + // Perform topological sort + // FIXME : Sort nodes and pass sorted indices in configure all nodes + + // Validate all nodes + detail::validate_all_nodes(graph); + + // Configure all nodes + auto workload = detail::configure_all_nodes(graph, ctx); + ARM_COMPUTE_ERROR_ON_MSG(workload.tasks.empty(), "Could not configure all nodes!"); + + // Allocate all tensors + detail::allocate_all_tensors(graph); + + // Call accessors on all Const nodes + detail::call_all_const_node_accessors(graph); + + _workloads.insert(std::make_pair(graph.id(), std::move(workload))); + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Created workload for graph with ID : " << graph.id().get() << std::endl); + + // Finalize Graph context + ctx.finalize(); + + // Make first run + execute_graph(graph); + + // Release all unused const nodes + detail::release_unused_tensors(graph); +} + +void GraphManager::execute_graph(Graph &graph) +{ + // Check if graph is finalized + auto it = _workloads.find(graph.id()); + ARM_COMPUTE_ERROR_ON_MSG(it == std::end(_workloads), "Graph is not registered!"); + + // Call input accessors + detail::call_all_input_node_accessors(it->second); + + // Run graph + detail::call_all_tasks(it->second); + + // Call output accessors + detail::call_all_output_node_accessors(it->second); +} + +void GraphManager::invalidate_graph(Graph &graph) +{ + auto it = _workloads.find(graph.id()); + ARM_COMPUTE_ERROR_ON_MSG(it == std::end(_workloads), "Graph is not registered!"); + + _workloads.erase(it); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/INode.cpp b/src/graph/INode.cpp index c753f66b43..c1c18e5853 100644 --- a/src/graph/INode.cpp +++ b/src/graph/INode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -23,33 +23,171 @@ */ #include "arm_compute/graph/INode.h" -#include "arm_compute/core/CL/OpenCL.h" -#include "arm_compute/core/Validate.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/graph/Edge.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Tensor.h" -using namespace arm_compute::graph; +namespace arm_compute +{ +namespace graph +{ +// *INDENT-OFF* +// clang-format off +INode::INode() + : _graph(nullptr), _id(EmptyNodeID), _common_params({ "", Target::UNSPECIFIED}), + _outputs(), _input_edges(), _output_edges(), _assigned_target(Target::UNSPECIFIED) +{ +} +// clang-format on +// *INDENT-ON* -TargetHint INode::override_target_hint(TargetHint target_hint) const +void INode::set_graph(Graph *g) { - if(target_hint == TargetHint::OPENCL && !opencl_is_available()) + ARM_COMPUTE_ERROR_ON(g == nullptr); + _graph = g; +} + +void INode::set_id(NodeID id) +{ + _id = id; +} + +void INode::set_common_node_parameters(NodeParams common_params) +{ + _common_params = std::move(common_params); +} + +void INode::set_requested_target(Target target) +{ + _common_params.target = target; +} + +void INode::set_assigned_target(Target target) +{ + _assigned_target = target; +} + +void INode::set_output_tensor(TensorID tid, size_t idx) +{ + if(tid != NullTensorID && (idx < _outputs.size()) && (_graph->tensor(tid) != nullptr)) { - target_hint = TargetHint::DONT_CARE; + ARM_COMPUTE_ERROR_ON(_graph == nullptr); + Tensor *updated_tensor = _graph->tensor(tid); + _outputs[idx] = tid; + + // Set tensor to all output edges of the node + for(auto &output_edge_id : _output_edges) + { + auto output_edge = _graph->edge(output_edge_id); + if(output_edge != nullptr) + { + // Unbind edge from current tensor + auto current_output_tensor = output_edge->tensor(); + current_output_tensor->unbind_edge(output_edge->id()); + + // Update tensor to edge and rebind tensor + output_edge->update_bound_tensor(updated_tensor); + updated_tensor->bind_edge(output_edge->id()); + } + } } - GraphHints hints{ target_hint }; - target_hint = node_override_hints(hints).target_hint(); - ARM_COMPUTE_ERROR_ON(target_hint == TargetHint::OPENCL && !opencl_is_available()); - return target_hint; } -bool INode::supports_in_place() const + +NodeID INode::id() const +{ + return _id; +} + +std::string INode::name() const +{ + return _common_params.name; +} + +const Graph *INode::graph() const +{ + return _graph; +} + +Graph *INode::graph() +{ + return _graph; +} + +const std::vector &INode::outputs() const { - return _supports_in_place; + return _outputs; } -void INode::set_supports_in_place(bool value) + +const std::vector &INode::input_edges() const +{ + return _input_edges; +} + +const std::set &INode::output_edges() const +{ + return _output_edges; +} + +TensorID INode::input_id(size_t idx) const +{ + ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size()); + Edge *e = _graph->edge(_input_edges[idx]); + return (e != nullptr) ? e->tensor_id() : NullTensorID; +} + +TensorID INode::output_id(size_t idx) const +{ + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + return _outputs[idx]; +} + +Tensor *INode::input(size_t idx) const +{ + ARM_COMPUTE_ERROR_ON(_graph == nullptr); + ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size()); + Edge *e = _graph->edge(_input_edges[idx]); + return (e != nullptr) ? e->tensor() : nullptr; +} + +Tensor *INode::output(size_t idx) const { - _supports_in_place = value; + ARM_COMPUTE_ERROR_ON(_graph == nullptr); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + return _graph->tensor(_outputs[idx]); } -GraphHints INode::node_override_hints(GraphHints hints) const + +EdgeID INode::input_edge_id(size_t idx) const +{ + ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size()); + return _input_edges[idx]; +} + +Edge *INode::input_edge(size_t idx) const +{ + ARM_COMPUTE_ERROR_ON(_graph == nullptr); + ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size()); + return _graph->edge(_input_edges[idx]); +} + +size_t INode::num_inputs() const +{ + return _input_edges.size(); +} + +size_t INode::num_outputs() const +{ + return _outputs.size(); +} + +Target INode::requested_target() const +{ + return _common_params.target; +} + +Target INode::assigned_target() const { - TargetHint target_hint = hints.target_hint(); - hints.set_target_hint((target_hint == TargetHint::DONT_CARE) ? TargetHint::NEON : target_hint); - return hints; + return _assigned_target; } +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/NodeContext.cpp b/src/graph/NodeContext.cpp deleted file mode 100644 index 2aa5aa13e8..0000000000 --- a/src/graph/NodeContext.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/NodeContext.h" - -using namespace arm_compute::graph; - -void NodeContext::set_target(TargetHint target) -{ - _target = target; -} - -void NodeContext::add_input(arm_compute::ITensor *input) -{ - ARM_COMPUTE_ERROR_ON(input == nullptr); - _inputs.emplace_back(input); -} - -void NodeContext::add_output(arm_compute::ITensor *output) -{ - ARM_COMPUTE_ERROR_ON(output == nullptr); - _outputs.emplace_back(output); -} - -OperationType NodeContext::operation() const -{ - return _operation; -} - -TargetHint NodeContext::target() const -{ - return _target; -} - -arm_compute::ITensor *NodeContext::input(size_t idx) const -{ - ARM_COMPUTE_ERROR_ON(idx >= _inputs.size()); - return _inputs[idx]; -} - -arm_compute::ITensor *NodeContext::output(size_t idx) const -{ - ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); - return _outputs[idx]; -} - -size_t NodeContext::num_inputs() const -{ - return _inputs.size(); -} - -size_t NodeContext::num_outputs() const -{ - return _outputs.size(); -} \ No newline at end of file diff --git a/src/graph/OperationRegistry.cpp b/src/graph/OperationRegistry.cpp deleted file mode 100644 index 651653f19c..0000000000 --- a/src/graph/OperationRegistry.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/OperationRegistry.h" - -using namespace arm_compute::graph; - -OperationRegistry::OperationRegistry() - : _registered_ops() -{ -} - -OperationRegistry &OperationRegistry::get() -{ - static OperationRegistry instance; - return instance; -} - -IOperation *OperationRegistry::find_operation(OperationType operation, TargetHint target) -{ - ARM_COMPUTE_ERROR_ON(!contains(operation, target)); - auto it = std::find_if(_registered_ops[operation].begin(), _registered_ops[operation].end(), [&](const std::unique_ptr &op) - { - return (op->target() == target); - }); - ARM_COMPUTE_ERROR_ON(it == _registered_ops[operation].end()); - return (*it).get(); -} - -bool OperationRegistry::contains(OperationType operation, TargetHint target) const -{ - auto it = _registered_ops.find(operation); - if(it != _registered_ops.end()) - { - return std::any_of(it->second.begin(), it->second.end(), [&](const std::unique_ptr &op) - { - return (op->target() == target); - }); - } - return false; -} diff --git a/src/graph/PassManager.cpp b/src/graph/PassManager.cpp new file mode 100644 index 0000000000..8ed68bd99b --- /dev/null +++ b/src/graph/PassManager.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/PassManager.h" + +#include "arm_compute/graph/Logger.h" + +namespace arm_compute +{ +namespace graph +{ +PassManager::PassManager() + : _passes() +{ +} + +const std::vector> &PassManager::passes() const +{ + return _passes; +} + +IGraphMutator *PassManager::pass(size_t index) +{ + return (index >= _passes.size()) ? nullptr : _passes.at(index).get(); +} + +void PassManager::append(std::unique_ptr pass) +{ + if(pass) + { + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Appending mutating pass : " << pass->name() << std::endl); + _passes.push_back(std::move(pass)); + } +} + +void PassManager::clear() +{ + _passes.clear(); +} + +void PassManager::run_all(Graph &g) +{ + for(auto &pass : _passes) + { + if(pass) + { + ARM_COMPUTE_LOG_GRAPH_INFO("Running mutating pass : " << pass->name() << std::endl); + pass->mutate(g); + } + } +} + +void PassManager::run(Graph &g, size_t index) +{ + if(index >= _passes.size()) + { + return; + } + + auto &pass = _passes.at(index); + + if(pass != nullptr) + { + pass->mutate(g); + } +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/SubGraph.cpp b/src/graph/SubGraph.cpp deleted file mode 100644 index b1cbb9cc95..0000000000 --- a/src/graph/SubGraph.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/SubGraph.h" - -#include "arm_compute/graph/Graph.h" -#include "arm_compute/graph/INode.h" -#include "arm_compute/graph/Tensor.h" - -using namespace arm_compute::graph; - -SubGraph::SubGraph() - : _nodes(), _input(nullptr), _output(nullptr) -{ -} - -void SubGraph::add_node(std::unique_ptr node) -{ - _nodes.push_back(std::move(node)); -} - -void SubGraph::add_tensor_object(std::unique_ptr tensor) -{ - // If it's the first Tensor added then it will be the input of the Graph. - if(_input == nullptr) - { - _input = std::move(tensor); - } - else - { - _output = std::move(tensor); - } -} - -std::unique_ptr SubGraph::construct(const GraphContext &ctx, std::unique_ptr input, std::unique_ptr output) -{ - auto graph = arm_compute::support::cpp14::make_unique(); - - // Set hint - // TODO(geopin01): store hints of sub-graph - graph->hints() = ctx.hints(); - - // Configure input - if(_input == nullptr) - { - _input = std::move(input); - } - graph->add_tensor_object(std::move(_input)); - - // Make sure first and last nodes of the subgraph always do operations out-of-place - _nodes.front()->set_supports_in_place(false); - _nodes.back()->set_supports_in_place(false); - - // Construct nodes - for(auto &node : _nodes) - { - graph->add_node(std::move(node)); - } - - // Configure output - if(_output == nullptr) - { - _output = std::move(output); - } - graph->add_tensor_object(std::move(_output)); - - return graph; -} - -bool SubGraph::has_input() const -{ - return _input != nullptr; -} - -bool SubGraph::has_output() const -{ - return _output != nullptr; -} - -SubGraph &arm_compute::graph::operator<<(SubGraph &graph, Tensor &&tensor) -{ - graph.add_tensor_object(arm_compute::support::cpp14::make_unique(std::move(tensor))); - return graph; -} - -SubGraph &arm_compute::graph::operator<<(SubGraph &graph, SubTensor &&sub_tensor) -{ - graph.add_tensor_object(arm_compute::support::cpp14::make_unique(std::move(sub_tensor))); - return graph; -} diff --git a/src/graph/SubTensor.cpp b/src/graph/SubTensor.cpp deleted file mode 100644 index 2e640dd93c..0000000000 --- a/src/graph/SubTensor.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/SubTensor.h" - -#include "arm_compute/core/Error.h" -#include "arm_compute/core/Helpers.h" -#include "arm_compute/core/Validate.h" -#include "arm_compute/runtime/CL/CLSubTensor.h" -#include "arm_compute/runtime/CL/CLTensor.h" -#include "arm_compute/runtime/SubTensor.h" -#include "arm_compute/runtime/Tensor.h" -#include "utils/TypePrinter.h" - -using namespace arm_compute::graph; - -namespace -{ -template -std::unique_ptr initialise_subtensor(arm_compute::ITensor *parent, TensorShape shape, Coordinates coords, bool extend_parent) -{ - auto ptensor = dynamic_cast(parent); - auto subtensor = arm_compute::support::cpp14::make_unique(ptensor, shape, coords, extend_parent); - return std::move(subtensor); -} -} // namespace - -SubTensor::SubTensor() - : _target(TargetHint::DONT_CARE), _tensor_shape(), _coords(), _parent(nullptr), _subtensor(nullptr), _extend_parent(false) -{ -} - -SubTensor::SubTensor(Tensor &parent, TensorShape tensor_shape, Coordinates coords, bool extend_parent) - : _target(TargetHint::DONT_CARE), _tensor_shape(tensor_shape), _coords(coords), _parent(nullptr), _subtensor(nullptr), _extend_parent(extend_parent) -{ - ARM_COMPUTE_ERROR_ON(parent.tensor() == nullptr); - _parent = parent.tensor(); - _target = parent.target(); - - instantiate_subtensor(); -} - -SubTensor::SubTensor(arm_compute::ITensor *parent, TensorShape tensor_shape, Coordinates coords, TargetHint target, bool extend_parent) - : _target(target), _tensor_shape(tensor_shape), _coords(coords), _parent(parent), _subtensor(nullptr), _extend_parent(extend_parent) -{ - ARM_COMPUTE_ERROR_ON(parent == nullptr); - instantiate_subtensor(); -} - -bool SubTensor::call_accessor() -{ - return true; -} - -bool SubTensor::has_accessor() const -{ - return false; -} - -arm_compute::ITensor *SubTensor::set_target(TargetHint target) -{ - ARM_COMPUTE_ERROR_ON(target != _target); - return (target == _target) ? _subtensor.get() : nullptr; -} - -arm_compute::ITensor *SubTensor::tensor() -{ - return _subtensor.get(); -} - -const arm_compute::ITensor *SubTensor::tensor() const -{ - return _subtensor.get(); -} - -TargetHint SubTensor::target() const -{ - return _target; -} - -void SubTensor::allocate() -{ - // NOP for sub-tensors -} - -void SubTensor::instantiate_subtensor() -{ - switch(_target) - { - case TargetHint::OPENCL: - _subtensor = initialise_subtensor(_parent, _tensor_shape, _coords, _extend_parent); - break; - case TargetHint::NEON: - _subtensor = initialise_subtensor(_parent, _tensor_shape, _coords, _extend_parent); - break; - default: - ARM_COMPUTE_ERROR("Invalid TargetHint"); - } -} diff --git a/src/graph/Tensor.cpp b/src/graph/Tensor.cpp index 4db79e93ad..47fb5c65bc 100644 --- a/src/graph/Tensor.cpp +++ b/src/graph/Tensor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -23,138 +23,89 @@ */ #include "arm_compute/graph/Tensor.h" -#include "arm_compute/core/Error.h" -#include "arm_compute/core/Helpers.h" -#include "arm_compute/core/Validate.h" -#include "arm_compute/runtime/CL/CLTensor.h" -#include "arm_compute/runtime/Tensor.h" -#include "utils/TypePrinter.h" - -using namespace arm_compute::graph; - -namespace +namespace arm_compute { -template -std::unique_ptr initialise_tensor(TensorInfo &info) +namespace graph { - auto tensor = arm_compute::support::cpp14::make_unique(); - tensor->allocator()->init(info); - return std::move(tensor); -} - -template -void tensor_allocate(arm_compute::ITensor &tensor) +Tensor::Tensor(TensorID id, TensorDescriptor desc) + : _id(id), _desc(desc), _handle(nullptr), _accessor(nullptr), _bound_edges() { - auto itensor = dynamic_cast(&tensor); - ARM_COMPUTE_ERROR_ON_NULLPTR(itensor); - itensor->allocator()->allocate(); } -} // namespace -Tensor::Tensor(TensorInfo &&info) - : _target(TargetHint::DONT_CARE), _info(info), _accessor(nullptr), _tensor(nullptr) +TensorID Tensor::id() const { + return _id; } -Tensor::Tensor(Tensor &&src) noexcept - : _target(src._target), - _info(std::move(src._info)), - _accessor(std::move(src._accessor)), - _tensor(std::move(src._tensor)) +TensorDescriptor &Tensor::desc() { + return _desc; } -void Tensor::set_info(TensorInfo &&info) +const TensorDescriptor &Tensor::desc() const { - _info = info; -} - -bool Tensor::call_accessor() -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(_accessor.get()); - auto cl_tensor = dynamic_cast(_tensor.get()); - if(cl_tensor != nullptr && cl_tensor->buffer() == nullptr) - { - cl_tensor->map(); - } - bool retval = _accessor->access_tensor(*_tensor); - if(cl_tensor != nullptr) - { - cl_tensor->unmap(); - } - return retval; + return _desc; } -bool Tensor::has_accessor() const +void Tensor::set_handle(std::unique_ptr backend_tensor) { - return (_accessor != nullptr); + _handle = std::move(backend_tensor); } -arm_compute::ITensor *Tensor::tensor() +ITensorHandle *Tensor::handle() { - return _tensor.get(); + return _handle.get(); } -const arm_compute::ITensor *Tensor::tensor() const +void Tensor::set_accessor(std::unique_ptr accessor) { - return _tensor.get(); + _accessor = std::move(accessor); } -const TensorInfo &Tensor::info() const +ITensorAccessor *Tensor::accessor() { - return _info; + return _accessor.get(); } -arm_compute::ITensor *Tensor::set_target(TargetHint target) +bool Tensor::call_accessor() { - if(_tensor != nullptr) + // Early exit guard + if(!_accessor || !_handle) { - ARM_COMPUTE_ERROR_ON(target != _target); + return false; } - else + + // Map tensor + _handle->map(true); + + // Return in case of null backend buffer + if(_handle->tensor().buffer() == nullptr) { - switch(target) - { - case TargetHint::OPENCL: - _tensor = initialise_tensor(_info); - break; - case TargetHint::NEON: - _tensor = initialise_tensor(_info); - break; - default: - ARM_COMPUTE_ERROR("Invalid TargetHint"); - } - _target = target; + return false; } - return _tensor.get(); + + // Call accessor + _accessor->access_tensor(_handle->tensor()); + + // Unmap tensor + _handle->unmap(); + + return true; } -void Tensor::allocate() +void Tensor::bind_edge(EdgeID eid) { - ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor.get()); - switch(_target) - { - case TargetHint::OPENCL: - tensor_allocate(*_tensor); - break; - case TargetHint::NEON: - tensor_allocate(*_tensor); - break; - default: - ARM_COMPUTE_ERROR("Invalid TargetHint"); - } + _bound_edges.insert(eid); } -void Tensor::allocate_and_fill_if_needed() +void Tensor::unbind_edge(EdgeID eid) { - allocate(); - if(_accessor != nullptr) - { - call_accessor(); - } + _bound_edges.erase(eid); } -TargetHint Tensor::target() const +const std::set Tensor::bound_edges() const { - return _target; + return _bound_edges; } +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/Utils.cpp b/src/graph/Utils.cpp new file mode 100644 index 0000000000..8537bbfb2a --- /dev/null +++ b/src/graph/Utils.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Utils.h" + +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/backends/BackendRegistry.h" +#include "arm_compute/graph/mutators/GraphMutators.h" + +namespace arm_compute +{ +namespace graph +{ +bool is_target_supported(Target target) +{ + return backends::BackendRegistry::get().contains(target) && backends::BackendRegistry::get().find_backend(target)->is_backend_supported(); +} + +Target get_default_target() +{ + if(is_target_supported(Target::NEON)) + { + return Target::NEON; + } + if(is_target_supported(Target::CL)) + { + return Target::CL; + } + if(is_target_supported(Target::GC)) + { + return Target::GC; + } + ARM_COMPUTE_ERROR("No backend exists!"); +} + +void force_target_to_graph(Graph &g, Target target) +{ + auto &nodes = g.nodes(); + for(auto &node : nodes) + { + if(node) + { + node->set_assigned_target(target); + } + } + + auto &tensors = g.tensors(); + for(auto &tensor : tensors) + { + if(tensor) + { + tensor->desc().target = target; + } + } +} + +PassManager create_default_pass_manager(Target target) +{ + PassManager pm; + + if(target != Target::GC) + { + pm.append(support::cpp14::make_unique()); + pm.append(support::cpp14::make_unique()); + pm.append(support::cpp14::make_unique()); + pm.append(support::cpp14::make_unique()); + } + + return pm; +} + +/** Default setups a graph Context + * + * @param[in] ctx Context to default initialize + */ +void setup_default_graph_context(GraphContext &ctx) +{ + for(const auto &backend : backends::BackendRegistry::get().backends()) + { + backend.second->setup_backend_context(ctx); + } +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/Workload.cpp b/src/graph/Workload.cpp new file mode 100644 index 0000000000..c53a8a42da --- /dev/null +++ b/src/graph/Workload.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Workload.h" + +#include "arm_compute/graph/INode.h" +#include "arm_compute/graph/ITensorHandle.h" + +namespace arm_compute +{ +namespace graph +{ +void ExecutionTask::operator()() +{ + if(task) + { + task->run(); + } +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/BackendRegistry.cpp b/src/graph/backends/BackendRegistry.cpp new file mode 100644 index 0000000000..2803322e64 --- /dev/null +++ b/src/graph/backends/BackendRegistry.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/BackendRegistry.h" + +using namespace arm_compute::graph::backends; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +BackendRegistry::BackendRegistry() + : _registered_backends() +{ +} + +BackendRegistry &BackendRegistry::get() +{ + static BackendRegistry instance; + return instance; +} + +IDeviceBackend *BackendRegistry::find_backend(Target target) +{ + ARM_COMPUTE_ERROR_ON(!contains(target)); + return _registered_backends[target].get(); +} + +bool BackendRegistry::contains(Target target) const +{ + auto it = _registered_backends.find(target); + return (it != _registered_backends.end()); +} + +const std::map> &BackendRegistry::backends() const +{ + return _registered_backends; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/backends/CL/CLDeviceBackend.cpp b/src/graph/backends/CL/CLDeviceBackend.cpp new file mode 100644 index 0000000000..f10eb33a98 --- /dev/null +++ b/src/graph/backends/CL/CLDeviceBackend.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/CL/CLDeviceBackend.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/INode.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/Tensor.h" +#include "arm_compute/graph/backends/BackendRegistrar.h" +#include "arm_compute/graph/backends/CL/CLFunctionFactory.h" +#include "arm_compute/graph/backends/CL/CLNodeValidator.h" +#include "arm_compute/graph/backends/CL/CLSubTensorHandle.h" +#include "arm_compute/graph/backends/CL/CLTensorHandle.h" + +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/runtime/BlobLifetimeManager.h" +#include "arm_compute/runtime/CL/CLBufferAllocator.h" +#include "arm_compute/runtime/CL/CLScheduler.h" +#include "arm_compute/runtime/MemoryManagerOnDemand.h" +#include "arm_compute/runtime/PoolManager.h" + +#include "support/ToolchainSupport.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +namespace +{ +bool file_exists(const std::string &filename) +{ + std::ifstream file(filename); + return file.good(); +} +} // namespace + +/** Register CL backend */ +static detail::BackendRegistrar CLDeviceBackend_registrar(Target::CL); + +/** Tuner export file */ +static const std::string tuner_data_filename = "acl_tuner.csv"; + +CLDeviceBackend::CLDeviceBackend() + : _tuner(), _allocator(cl::Context::getDefault()) +{ +} + +CLDeviceBackend::~CLDeviceBackend() +{ + // TODO (geopin01) : Shouldn't call non exception safe stuff here + if(_tuner.tune_new_kernels() && !_tuner.lws_table().empty()) + { + _tuner.save_to_file(tuner_data_filename); + } +} + +void CLDeviceBackend::set_kernel_tuning(bool enable_tuning) +{ + _tuner.set_tune_new_kernels(enable_tuning); +} + +void CLDeviceBackend::initialize_backend() +{ + // Load tuner data if available + if(_tuner.lws_table().empty() && file_exists(tuner_data_filename)) + { + _tuner.load_from_file(tuner_data_filename); + } + + // Setup Scheduler + CLScheduler::get().default_init(&_tuner); + + // Create allocator with new context + _allocator = CLBufferAllocator(); +} + +void CLDeviceBackend::setup_backend_context(GraphContext &ctx) +{ + // Setup tuner + set_kernel_tuning(ctx.config().use_tuner); + + // Setup a management backend + if(ctx.memory_management_ctx(Target::CL) == nullptr) + { + MemoryManagerContext mm_ctx; + mm_ctx.target = Target::CL; + mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer); + + ctx.insert_memory_management_ctx(std::move(mm_ctx)); + } +} + +bool CLDeviceBackend::is_backend_supported() +{ + return arm_compute::opencl_is_available(); +} + +std::unique_ptr CLDeviceBackend::create_tensor(const Tensor &tensor) +{ + // Get tensor descriptor + const TensorDescriptor &tensor_desc = tensor.desc(); + ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::CL); + + // Create backend tensor handle + TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type); + auto backend_tensor_handle = support::cpp14::make_unique(info); + + return std::move(backend_tensor_handle); +} + +std::unique_ptr CLDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) +{ + if(parent == nullptr) + { + return nullptr; + } + + return support::cpp14::make_unique(parent, shape, coords, extend_parent); +} + +std::unique_ptr CLDeviceBackend::configure_node(INode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring CL node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::CL); + + // Configure node + return CLFunctionFactory::create(&node, ctx); +} + +arm_compute::Status CLDeviceBackend::validate_node(INode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating CL node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::CL); + + return CLNodeValidator::validate(&node); +} + +std::shared_ptr CLDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity) +{ + if(affinity == MemoryManagerAffinity::Offset) + { + ARM_COMPUTE_LOG_GRAPH_WARNING("CL Backend does not support offset affinity memory management!"); + return nullptr; + } + + auto lifetime_mgr = std::make_shared(); + auto pool_mgr = std::make_shared(); + auto mm = std::make_shared(lifetime_mgr, pool_mgr); + + mm->set_allocator(&_allocator); + + return mm; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/CL/CLFunctionsFactory.cpp b/src/graph/backends/CL/CLFunctionsFactory.cpp new file mode 100644 index 0000000000..1b448fefd2 --- /dev/null +++ b/src/graph/backends/CL/CLFunctionsFactory.cpp @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/CL/CLFunctionFactory.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/TypePrinter.h" +#include "arm_compute/graph/Types.h" +#include "arm_compute/graph/backends/Utils.h" +#include "arm_compute/graph/nodes/Nodes.h" +#include "arm_compute/runtime/CL/CLFunctions.h" + +#include "support/ToolchainSupport.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +namespace +{ +/** Returns backing tensor of a given tensor + * + * @param[in] tensor Tensor to extract the backing tensor from + * + * @return Backing tensor if present else nullptr + */ +arm_compute::ICLTensor *get_backing_tensor(arm_compute::graph::Tensor *tensor) +{ + arm_compute::ICLTensor *backing_tensor = nullptr; + if(tensor != nullptr) + { + ARM_COMPUTE_ERROR_ON(tensor->desc().target != arm_compute::graph::Target::CL); + // Get backing tensor handle + ITensorHandle *tensor_handle = tensor->handle(); + // Get backing tensor + backing_tensor = (tensor_handle != nullptr) ? polymorphic_cast(&tensor_handle->tensor()) : nullptr; + } + + return backing_tensor; +} + +/** Create a backend activation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend activation layer function + */ +std::unique_ptr create_activation_layer(ActivationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const ActivationLayerInfo act_info = node.activation_info(); + + // Create function + auto func = support::cpp14::make_unique(); + func->configure(input, output, act_info); + + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLActivationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Activation function: " << act_info.activation() + << " a: " << act_info.a() + << " b: " << act_info.b() + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend batch normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend batch normalization layer function + */ +std::unique_ptr create_batch_normalization_layer(BatchNormalizationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + + // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *mean = get_backing_tensor(node.input(1)); + ICLTensor *var = get_backing_tensor(node.input(2)); + ICLTensor *beta = get_backing_tensor(node.input(3)); + ICLTensor *gamma = get_backing_tensor(node.input(4)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const float epsilon = node.epsilon(); + const ActivationLayerInfo fused_act = node.fused_activation(); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLBatchNormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Epsilon: " << epsilon << " " + << (fused_act.enabled() ? to_string(fused_act.activation()) : "") + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend convolution layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend convolution layer function + */ +std::unique_ptr create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *weights = get_backing_tensor(node.input(1)); + ICLTensor *biases = get_backing_tensor(node.input(2)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const ConvolutionMethod conv_algorithm = node.convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::shared_ptr mm = get_memory_manager(ctx, Target::CL); + std::unique_ptr func; + std::string func_name; + + if(conv_algorithm == ConvolutionMethod::WINOGRAD) + { + std::tie(func, func_name) = create_named_function( + std::string("CLWinogradConvolutionLayer"), input, weights, biases, output, conv_info); + } + else if(conv_algorithm == ConvolutionMethod::DIRECT) + { + std::tie(func, func_name) = create_named_function( + std::string("CLDirectConvolutionLayer"), input, weights, biases, output, conv_info); + } + else if(conv_algorithm == ConvolutionMethod::GEMM) + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("CLGEMMConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + else + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("CLConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend layer depth concatenate function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth concatenate layer function + */ +std::unique_ptr create_depth_concatenate_layer(DepthConcatenateLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Return nullptr if depth concatenate is switched off + if(!node.is_enabled()) + { + return nullptr; + } + + // Extract IO and info + std::vector inputs; + for(unsigned int i = 0; i < node.num_inputs(); ++i) + { + inputs.push_back(get_backing_tensor(node.input(i))); + } + ICLTensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(inputs, output); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLDepthConcatenateLayer" + << " Data Type: " << output->info()->data_type() + << " Shape: " << output->info()->tensor_shape() + << " Num Inputs: " << inputs.size() + << std::endl); + + return std::move(func); +} + +/** Create a backend layer depth-wise convolution function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth-wise convolution layer function + */ +std::unique_ptr create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *weights = get_backing_tensor(node.input(1)); + ICLTensor *biases = get_backing_tensor(node.input(2)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::unique_ptr func; + std::string func_name; + if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3) + { + std::tie(func, func_name) = create_named_function( + std::string("CLDepthwiseConvolutionLayer3x3"), input, weights, biases, output, conv_info); + } + else + { + std::tie(func, func_name) = create_named_function( + std::string("CLDepthwiseConvolutionLayer"), input, weights, biases, output, conv_info); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend element-wise operation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend element-wise operation layer function + */ +std::unique_ptr create_eltwise_layer(EltwiseLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input1 = get_backing_tensor(node.input(0)); + ICLTensor *input2 = get_backing_tensor(node.input(1)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const EltwiseOperation eltwise_op = node.eltwise_operation(); + ARM_COMPUTE_ERROR_ON(input1 == nullptr); + ARM_COMPUTE_ERROR_ON(input2 == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + std::unique_ptr func = nullptr; + std::string func_name; + if(eltwise_op == EltwiseOperation::ADD) + { + std::tie(func, func_name) = create_named_function(std::string("CLArithmeticAddition"), + input1, input2, output, + ConvertPolicy::SATURATE); + } + else if(eltwise_op == EltwiseOperation::SUB) + { + std::tie(func, func_name) = create_named_function( + std::string("CLArithmeticSubtraction"), input1, input2, output, ConvertPolicy::SATURATE); + } + else if(eltwise_op == EltwiseOperation::MUL) + { + std::tie(func, func_name) = create_named_function( + std::string("CLPixelWiseMultiplication"), input1, input2, output, 1.f, ConvertPolicy::SATURATE, + RoundingPolicy::TO_NEAREST_EVEN); + } + else + { + ARM_COMPUTE_ERROR("Unsupported element-wise operation!"); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input1->info()->data_type() + << " Shape : " << input1->info()->tensor_shape() + << std::endl); + + return func; +} + +/** Create a backend flatten layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend flatten layer function + */ +std::unique_ptr create_flatten_layer(FlattenLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL FlattenLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLFlattenLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend fully connected layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend fully connected layer function + */ +std::unique_ptr create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *weights = get_backing_tensor(node.input(1)); + ICLTensor *biases = get_backing_tensor(node.input(2)); + ICLTensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::CL)); + func->configure(input, weights, biases, output); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(weights == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLFullyConnectedLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Biases Shape: " << biases->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend normalization layer function + */ +std::unique_ptr create_normalization_layer(NormalizationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const NormalizationLayerInfo norm_info = node.normalization_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, norm_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLNormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Normalization info: " << norm_info.type() + << std::endl); + + return std::move(func); +} + +/** Create a backend pooling layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend pooling layer function + */ +std::unique_ptr create_pooling_layer(PoolingLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const PoolingLayerInfo pool_info = node.pooling_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, pool_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLPoolingLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Pooling info: " << pool_info.pool_type() + << std::endl); + + return std::move(func); +} + +/** Create a backend reshape layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend reshape layer function + */ +std::unique_ptr create_reshape_layer(ReshapeLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL ReshapeLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLReshapeLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend softmax layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend softmax layer function + */ +std::unique_ptr create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating CL SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ICLTensor *input = get_backing_tensor(node.input(0)); + ICLTensor *output = get_backing_tensor(node.output(0)); + const float beta = node.beta(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::CL)); + func->configure(input, output, beta); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLSoftmaxLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} +} // namespace + +std::unique_ptr CLFunctionFactory::create(INode *node, GraphContext &ctx) +{ + if(node == nullptr) + { + return nullptr; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ActivationLayer: + return create_activation_layer(*polymorphic_downcast(node)); + case NodeType::BatchNormalizationLayer: + return create_batch_normalization_layer(*polymorphic_downcast(node)); + case NodeType::ConvolutionLayer: + return create_convolution_layer(*polymorphic_downcast(node), ctx); + case NodeType::DepthConcatenateLayer: + return create_depth_concatenate_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return create_depthwise_convolution_layer(*polymorphic_downcast(node)); + case NodeType::EltwiseLayer: + return create_eltwise_layer(*polymorphic_downcast(node)); + case NodeType::FlattenLayer: + return create_flatten_layer(*polymorphic_downcast(node)); + case NodeType::FullyConnectedLayer: + return create_fully_connected_layer(*polymorphic_downcast(node), ctx); + case NodeType::NormalizationLayer: + return create_normalization_layer(*polymorphic_downcast(node)); + case NodeType::PoolingLayer: + return create_pooling_layer(*polymorphic_downcast(node)); + case NodeType::ReshapeLayer: + return create_reshape_layer(*polymorphic_downcast(node)); + case NodeType::SoftmaxLayer: + return create_softmax_layer(*polymorphic_downcast(node), ctx); + default: + return nullptr; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/CL/CLNodeValidator.cpp b/src/graph/backends/CL/CLNodeValidator.cpp new file mode 100644 index 0000000000..c16b2e67df --- /dev/null +++ b/src/graph/backends/CL/CLNodeValidator.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/CL/CLNodeValidator.h" + +#include "arm_compute/graph/backends/ValidateHelpers.h" +#include "arm_compute/graph/nodes/Nodes.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/runtime/CL/CLFunctions.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +Status CLNodeValidator::validate(INode *node) +{ + if(node == nullptr) + { + return Status{}; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ConvolutionLayer: + return detail::validate_convolution_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return detail::validate_depthwise_convolution_layer(*polymorphic_downcast(node)); + default: + return Status{}; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/CL/CLSubTensorHandle.cpp b/src/graph/backends/CL/CLSubTensorHandle.cpp new file mode 100644 index 0000000000..a1bc8a1dd3 --- /dev/null +++ b/src/graph/backends/CL/CLSubTensorHandle.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/CL/CLSubTensorHandle.h" + +#include "arm_compute/core/utils/misc/Cast.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +CLSubTensorHandle::CLSubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent) + : _sub_tensor() +{ + ARM_COMPUTE_ERROR_ON(!parent_handle); + auto parent_tensor = arm_compute::utils::cast::polymorphic_downcast(&parent_handle->tensor()); + _sub_tensor = arm_compute::CLSubTensor(parent_tensor, shape, coords, extend_parent); +} + +void CLSubTensorHandle::allocate() +{ + // noop +} + +const arm_compute::ITensor &CLSubTensorHandle::tensor() const +{ + return _sub_tensor; +} + +arm_compute::ITensor &CLSubTensorHandle::tensor() +{ + return _sub_tensor; +} + +void CLSubTensorHandle::map(bool blocking) +{ + _sub_tensor.map(blocking); +} + +void CLSubTensorHandle::unmap() +{ + _sub_tensor.unmap(); +} + +void CLSubTensorHandle::release_if_unused() +{ + // noop +} + +bool CLSubTensorHandle::is_subtensor() const +{ + return true; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/CL/CLTensorHandle.cpp b/src/graph/backends/CL/CLTensorHandle.cpp new file mode 100644 index 0000000000..563c4d9ac6 --- /dev/null +++ b/src/graph/backends/CL/CLTensorHandle.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/CL/CLTensorHandle.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +CLTensorHandle::CLTensorHandle(const ITensorInfo &info) + : _tensor() +{ + _tensor.allocator()->init(info); +} + +void CLTensorHandle::allocate() +{ + _tensor.allocator()->allocate(); +} + +const arm_compute::ITensor &CLTensorHandle::tensor() const +{ + return _tensor; +} + +arm_compute::ITensor &CLTensorHandle::tensor() +{ + return _tensor; +} + +void CLTensorHandle::map(bool blocking) +{ + _tensor.map(blocking); +} + +void CLTensorHandle::unmap() +{ + _tensor.unmap(); +} + +void CLTensorHandle::release_if_unused() +{ + // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used + if(!_tensor.is_used()) + { + _tensor.allocator()->free(); + } +} + +bool CLTensorHandle::is_subtensor() const +{ + return false; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/GLES/GCDeviceBackend.cpp b/src/graph/backends/GLES/GCDeviceBackend.cpp new file mode 100644 index 0000000000..8cd9994744 --- /dev/null +++ b/src/graph/backends/GLES/GCDeviceBackend.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/GLES/GCDeviceBackend.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/INode.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/Tensor.h" +#include "arm_compute/graph/backends/BackendRegistrar.h" +#include "arm_compute/graph/backends/GLES/GCFunctionFactory.h" +#include "arm_compute/graph/backends/GLES/GCNodeValidator.h" +#include "arm_compute/graph/backends/GLES/GCTensorHandle.h" + +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/runtime/BlobLifetimeManager.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCBufferAllocator.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCScheduler.h" +#include "arm_compute/runtime/MemoryManagerOnDemand.h" +#include "arm_compute/runtime/PoolManager.h" + +#include "support/ToolchainSupport.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +/** Register GLES backend */ +static detail::BackendRegistrar GCDeviceBackend_registrar(Target::GC); + +GCDeviceBackend::GCDeviceBackend() + : _allocator() +{ +} + +void GCDeviceBackend::initialize_backend() +{ + // Setup Scheduler + GCScheduler::get().default_init(); +} + +void GCDeviceBackend::setup_backend_context(GraphContext &ctx) +{ + // Setup a management backend + if(ctx.memory_management_ctx(Target::GC) == nullptr) + { + MemoryManagerContext mm_ctx; + mm_ctx.target = Target::GC; + mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer); + + ctx.insert_memory_management_ctx(std::move(mm_ctx)); + } +} + +bool GCDeviceBackend::is_backend_supported() +{ + return arm_compute::opengles31_is_available(); +} + +std::unique_ptr GCDeviceBackend::create_tensor(const Tensor &tensor) +{ + // Get tensor descriptor + const TensorDescriptor &tensor_desc = tensor.desc(); + ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::GC); + + // Create backend tensor handle + TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type); + auto backend_tensor_handle = support::cpp14::make_unique(info); + + return std::move(backend_tensor_handle); +} + +std::unique_ptr GCDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) +{ + ARM_COMPUTE_UNUSED(parent, shape, coords, extend_parent); + ARM_COMPUTE_ERROR("GLES backend has no sub-tensor support!"); + return nullptr; +} + +std::unique_ptr GCDeviceBackend::configure_node(INode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring GC node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::GC); + + // Configure node + return GCFunctionFactory::create(&node, ctx); +} + +arm_compute::Status GCDeviceBackend::validate_node(INode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating GC node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::GC); + + return GCNodeValidator::validate(&node); +} + +std::shared_ptr GCDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity) +{ + if(affinity == MemoryManagerAffinity::Offset) + { + ARM_COMPUTE_LOG_GRAPH_WARNING("GC Backend does not support offset affinity memory management!"); + return nullptr; + } + + auto lifetime_mgr = std::make_shared(); + auto pool_mgr = std::make_shared(); + auto mm = std::make_shared(lifetime_mgr, pool_mgr); + + mm->set_allocator(&_allocator); + + return mm; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/GLES/GCFunctionsFactory.cpp b/src/graph/backends/GLES/GCFunctionsFactory.cpp new file mode 100644 index 0000000000..12e7c042d4 --- /dev/null +++ b/src/graph/backends/GLES/GCFunctionsFactory.cpp @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/GLES/GCFunctionFactory.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/TypePrinter.h" +#include "arm_compute/graph/Types.h" +#include "arm_compute/graph/backends/Utils.h" +#include "arm_compute/graph/nodes/Nodes.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h" + +#include "support/ToolchainSupport.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +namespace +{ +/** Returns backing tensor of a given tensor + * + * @param[in] tensor Tensor to extract the backing tensor from + * + * @return Backing tensor if present else nullptr + */ +arm_compute::IGCTensor *get_backing_tensor(arm_compute::graph::Tensor *tensor) +{ + arm_compute::IGCTensor *backing_tensor = nullptr; + if(tensor != nullptr) + { + ARM_COMPUTE_ERROR_ON(tensor->desc().target != arm_compute::graph::Target::GC); + // Get backing tensor handle + ITensorHandle *tensor_handle = tensor->handle(); + // Get backing tensor + backing_tensor = (tensor_handle != nullptr) ? polymorphic_cast(&tensor_handle->tensor()) : nullptr; + } + + return backing_tensor; +} + +/** Create a backend activation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend activation layer function + */ +std::unique_ptr create_activation_layer(ActivationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const ActivationLayerInfo act_info = node.activation_info(); + + // Create function + auto func = support::cpp14::make_unique(); + func->configure(input, output, act_info); + + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCActivationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Activation function: " << act_info.activation() + << " a: " << act_info.a() + << " b: " << act_info.b() + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend batch normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend batch normalization layer function + */ +std::unique_ptr create_batch_normalization_layer(BatchNormalizationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + + // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *mean = get_backing_tensor(node.input(1)); + IGCTensor *var = get_backing_tensor(node.input(2)); + IGCTensor *beta = get_backing_tensor(node.input(3)); + IGCTensor *gamma = get_backing_tensor(node.input(4)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const float epsilon = node.epsilon(); + const ActivationLayerInfo fused_act = node.fused_activation(); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCBatchNormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Epsilon: " << epsilon << " " + << (fused_act.enabled() ? to_string(fused_act.activation()) : "") + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend convolution layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend convolution layer function + */ +std::unique_ptr create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *weights = get_backing_tensor(node.input(1)); + IGCTensor *biases = get_backing_tensor(node.input(2)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const ConvolutionMethod conv_algorithm = node.convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::shared_ptr mm = get_memory_manager(ctx, Target::GC); + std::unique_ptr func; + std::string func_name; + + if(conv_algorithm == ConvolutionMethod::DIRECT) + { + std::tie(func, func_name) = create_named_function( + std::string("GCDirectConvolutionLayer"), input, weights, biases, output, conv_info); + } + else + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("GCConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend layer depth concatenate function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth concatenate layer function + */ +std::unique_ptr create_depth_concatenate_layer(DepthConcatenateLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Return nullptr if depth concatenate is switched off + if(!node.is_enabled()) + { + return nullptr; + } + + // Extract IO and info + std::vector inputs; + for(unsigned int i = 0; i < node.num_inputs(); ++i) + { + inputs.push_back(get_backing_tensor(node.input(i))); + } + IGCTensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(inputs, output); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCDepthConcatenateLayer" + << " Data Type: " << output->info()->data_type() + << " Shape: " << output->info()->tensor_shape() + << " Num Inputs: " << inputs.size() + << std::endl); + + return std::move(func); +} + +/** Create a backend layer depth-wise convolution function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth-wise convolution layer function + */ +std::unique_ptr create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *weights = get_backing_tensor(node.input(1)); + IGCTensor *biases = get_backing_tensor(node.input(2)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::unique_ptr func; + std::string func_name; + if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3) + { + std::tie(func, func_name) = create_named_function( + std::string("GCDepthwiseConvolutionLayer3x3"), input, weights, biases, output, conv_info); + } + else + { + ARM_COMPUTE_ERROR("Generic DepthwiseConvolutionLayer is not supported in GLES backend"); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend element-wise operation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend element-wise operation layer function + */ +std::unique_ptr create_eltwise_layer(EltwiseLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input1 = get_backing_tensor(node.input(0)); + IGCTensor *input2 = get_backing_tensor(node.input(1)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const EltwiseOperation eltwise_op = node.eltwise_operation(); + ARM_COMPUTE_ERROR_ON(input1 == nullptr); + ARM_COMPUTE_ERROR_ON(input2 == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + std::unique_ptr func = nullptr; + std::string func_name; + if(eltwise_op == EltwiseOperation::ADD) + { + std::tie(func, func_name) = create_named_function(std::string("GCArithmeticAddition"), + input1, input2, output, + ConvertPolicy::SATURATE); + } + else if(eltwise_op == EltwiseOperation::SUB) + { + ARM_COMPUTE_ERROR("Arithmetic subtraction is not supported in GLES backend"); + } + else if(eltwise_op == EltwiseOperation::MUL) + { + std::tie(func, func_name) = create_named_function( + std::string("GCPixelWiseMultiplication"), input1, input2, output, 1.f); + } + else + { + ARM_COMPUTE_ERROR("Unsupported element-wise operation!"); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input1->info()->data_type() + << " Shape : " << input1->info()->tensor_shape() + << std::endl); + + return func; +} + +/** Create a backend fully connected layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend fully connected layer function + */ +std::unique_ptr create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name() + << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *weights = get_backing_tensor(node.input(1)); + IGCTensor *biases = get_backing_tensor(node.input(2)); + IGCTensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::GC)); + func->configure(input, weights, biases, output); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(weights == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCFullyConnectedLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Biases Shape: " << biases->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend normalization layer function + */ +std::unique_ptr create_normalization_layer(NormalizationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const NormalizationLayerInfo norm_info = node.normalization_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, norm_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCNormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Normalization info: " << norm_info.type() + << std::endl); + + return std::move(func); +} + +/** Create a backend pooling layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend pooling layer function + */ +std::unique_ptr create_pooling_layer(PoolingLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const PoolingLayerInfo pool_info = node.pooling_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, pool_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCPoolingLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Pooling info: " << pool_info.pool_type() + << std::endl); + + return std::move(func); +} + +/** Create a backend softmax layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend softmax layer function + */ +std::unique_ptr create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE( + "Creating GC SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + IGCTensor *input = get_backing_tensor(node.input(0)); + IGCTensor *output = get_backing_tensor(node.output(0)); + const float beta = node.beta(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::CL)); + func->configure(input, output, beta); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCSoftmaxLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} +} // namespace + +std::unique_ptr GCFunctionFactory::create(INode *node, GraphContext &ctx) +{ + if(node == nullptr) + { + return nullptr; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ActivationLayer: + return create_activation_layer(*polymorphic_downcast(node)); + case NodeType::BatchNormalizationLayer: + return create_batch_normalization_layer(*polymorphic_downcast(node)); + case NodeType::ConvolutionLayer: + return create_convolution_layer(*polymorphic_downcast(node), ctx); + case NodeType::DepthConcatenateLayer: + return create_depth_concatenate_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return create_depthwise_convolution_layer(*polymorphic_downcast(node)); + case NodeType::EltwiseLayer: + return create_eltwise_layer(*polymorphic_downcast(node)); + case NodeType::FullyConnectedLayer: + return create_fully_connected_layer(*polymorphic_downcast(node), ctx); + case NodeType::NormalizationLayer: + return create_normalization_layer(*polymorphic_downcast(node)); + case NodeType::PoolingLayer: + return create_pooling_layer(*polymorphic_downcast(node)); + case NodeType::SoftmaxLayer: + return create_softmax_layer(*polymorphic_downcast(node), ctx); + default: + return nullptr; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/GLES/GCNodeValidator.cpp b/src/graph/backends/GLES/GCNodeValidator.cpp new file mode 100644 index 0000000000..1e89265382 --- /dev/null +++ b/src/graph/backends/GLES/GCNodeValidator.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/GLES/GCNodeValidator.h" + +#include "arm_compute/graph/backends/ValidateHelpers.h" +#include "arm_compute/graph/nodes/Nodes.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +namespace +{ +/** Validates a Depthwise Convolution layer node + * + * @param[in] node Node to validate + * + * @return Status + */ +Status validate_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating GCDepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + arm_compute::ITensorInfo *weights = detail::get_backing_tensor_info(node.input(1)); + ARM_COMPUTE_ERROR_ON(weights == nullptr); + + // TODO (geopin01) : Switch when validation is implemented + // Validate function + ARM_COMPUTE_RETURN_ERROR_ON_MSG(weights->tensor_shape().x() != 3 && weights->tensor_shape().y() != 3, "Unsupported depthwise convolution"); + node.set_depthwise_convolution_method(DepthwiseConvolutionMethod::OPTIMIZED_3x3); + + return Status{}; +} +/** Validates a Convolution layer node + * + * @param[in] node Node to validate + * + * @return Status + */ +Status validate_convolution_layer(ConvolutionLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + arm_compute::ITensorInfo *weights = detail::get_backing_tensor_info(node.input(1)); + const PadStrideInfo conv_info = node.convolution_info(); + const ConvolutionMethod conv_algorithm = node.convolution_method(); + + // Validate function + if(conv_algorithm == ConvolutionMethod::DIRECT) + { + bool is_square = weights->tensor_shape().x() == weights->tensor_shape().y(); + bool is_direct = (weights->tensor_shape().x() == 1) || (weights->tensor_shape().x() == 3) || (weights->tensor_shape().x() == 5); + bool is_correct_stride = (conv_info.stride().first) <= 2 && (conv_info.stride().second <= 2); + if(!(is_square && is_direct && is_correct_stride)) + { + node.set_convolution_method(ConvolutionMethod::DEFAULT); + } + } + + return Status{}; +} +} // namespace + +Status GCNodeValidator::validate(INode *node) +{ + if(node == nullptr) + { + return Status{}; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ConvolutionLayer: + return validate_convolution_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return validate_depthwise_convolution_layer(*polymorphic_downcast(node)); + case NodeType::FlattenLayer: + return ARM_COMPUTE_CREATE_ERROR(arm_compute::ErrorCode::RUNTIME_ERROR, "Unsupported operation"); + case NodeType::ReshapeLayer: + return ARM_COMPUTE_CREATE_ERROR(arm_compute::ErrorCode::RUNTIME_ERROR, "Unsupported operation"); + default: + return Status{}; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/GLES/GCTensorHandle.cpp b/src/graph/backends/GLES/GCTensorHandle.cpp new file mode 100644 index 0000000000..ae7c778130 --- /dev/null +++ b/src/graph/backends/GLES/GCTensorHandle.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/GLES/GCTensorHandle.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +GCTensorHandle::GCTensorHandle(const ITensorInfo &info) + : _tensor() +{ + _tensor.allocator()->init(info); +} + +void GCTensorHandle::allocate() +{ + _tensor.allocator()->allocate(); +} + +const arm_compute::ITensor &GCTensorHandle::tensor() const +{ + return _tensor; +} + +arm_compute::ITensor &GCTensorHandle::tensor() +{ + return _tensor; +} + +void GCTensorHandle::map(bool blocking) +{ + _tensor.map(blocking); +} + +void GCTensorHandle::unmap() +{ + _tensor.unmap(); +} + +void GCTensorHandle::release_if_unused() +{ + // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used + if(!_tensor.is_used()) + { + _tensor.allocator()->free(); + } +} + +bool GCTensorHandle::is_subtensor() const +{ + return false; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/NEON/NEDeviceBackend.cpp b/src/graph/backends/NEON/NEDeviceBackend.cpp new file mode 100644 index 0000000000..87f88dffdf --- /dev/null +++ b/src/graph/backends/NEON/NEDeviceBackend.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/NEON/NEDeviceBackend.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/INode.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/Tensor.h" +#include "arm_compute/graph/backends/BackendRegistrar.h" +#include "arm_compute/graph/backends/NEON/NEFunctionFactory.h" +#include "arm_compute/graph/backends/NEON/NENodeValidator.h" +#include "arm_compute/graph/backends/NEON/NESubTensorHandle.h" +#include "arm_compute/graph/backends/NEON/NETensorHandle.h" + +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/runtime/Allocator.h" +#include "arm_compute/runtime/BlobLifetimeManager.h" +#include "arm_compute/runtime/MemoryManagerOnDemand.h" +#include "arm_compute/runtime/OffsetLifetimeManager.h" +#include "arm_compute/runtime/PoolManager.h" +#include "arm_compute/runtime/Scheduler.h" + +#include "support/ToolchainSupport.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +/** Register NEON backend */ +static detail::BackendRegistrar NEDeviceBackend_registrar(Target::NEON); + +NEDeviceBackend::NEDeviceBackend() + : _allocator() +{ +} + +void NEDeviceBackend::initialize_backend() +{ +} + +void NEDeviceBackend::setup_backend_context(GraphContext &ctx) +{ + // Set number of threads + Scheduler::get().set_num_threads(ctx.config().num_threads); + + // Create function level memory manager + if(ctx.memory_management_ctx(Target::NEON) == nullptr) + { + MemoryManagerContext mm_ctx; + mm_ctx.target = Target::NEON; + mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer); + + ctx.insert_memory_management_ctx(std::move(mm_ctx)); + } +} + +bool NEDeviceBackend::is_backend_supported() +{ + return true; +} + +std::unique_ptr NEDeviceBackend::create_tensor(const Tensor &tensor) +{ + // Get tensor descriptor + const TensorDescriptor &tensor_desc = tensor.desc(); + ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::NEON); + + // Create backend tensor handle + TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type); + auto backend_tensor_handle = support::cpp14::make_unique(info); + + return std::move(backend_tensor_handle); +} + +std::unique_ptr NEDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) +{ + if(parent == nullptr) + { + return nullptr; + } + + return support::cpp14::make_unique(parent, shape, coords, extend_parent); +} + +std::unique_ptr NEDeviceBackend::configure_node(INode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring NEON node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::NEON); + + // Configure node + return NEFunctionFactory::create(&node, ctx); +} + +arm_compute::Status NEDeviceBackend::validate_node(INode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating NEON node with ID : " << node.id() << std::endl); + ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::NEON); + + return NENodeValidator::validate(&node); +} + +std::shared_ptr NEDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity) +{ + std::shared_ptr lifetime_mgr = nullptr; + if(affinity == MemoryManagerAffinity::Buffer) + { + lifetime_mgr = std::make_shared(); + } + else + { + lifetime_mgr = std::make_shared(); + } + auto pool_mgr = std::make_shared(); + auto mm = std::make_shared(lifetime_mgr, pool_mgr); + + mm->set_allocator(&_allocator); + + return mm; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/NEON/NEFunctionFactory.cpp b/src/graph/backends/NEON/NEFunctionFactory.cpp new file mode 100644 index 0000000000..228af9ca6f --- /dev/null +++ b/src/graph/backends/NEON/NEFunctionFactory.cpp @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/NEON/NEFunctionFactory.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/TypePrinter.h" +#include "arm_compute/graph/backends/Utils.h" +#include "arm_compute/graph/nodes/Nodes.h" +#include "arm_compute/runtime/NEON/NEFunctions.h" +#include "support/ToolchainSupport.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +namespace +{ +/** Returns backing tensor of a given tensor + * + * @param[in] tensor Tensor to extract the backing tensor from + * + * @return Backing tensor if present else nullptr + */ +arm_compute::ITensor *get_backing_tensor(arm_compute::graph::Tensor *tensor) +{ + return ((tensor == nullptr) || (tensor->handle() == nullptr)) ? nullptr : &tensor->handle()->tensor(); +} + +/** Create a backend activation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend activation layer function + */ +std::unique_ptr create_activation_layer(ActivationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + const ActivationLayerInfo act_info = node.activation_info(); + + // Create function + auto func = support::cpp14::make_unique(); + func->configure(input, output, act_info); + + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEActivationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Activation function: " << act_info.activation() + << " a: " << act_info.a() + << " b: " << act_info.b() + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend batch normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend batch normalization layer function + */ +std::unique_ptr create_batch_normalization_layer(BatchNormalizationLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + + // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *mean = get_backing_tensor(node.input(1)); + ITensor *var = get_backing_tensor(node.input(2)); + ITensor *beta = get_backing_tensor(node.input(3)); + ITensor *gamma = get_backing_tensor(node.input(4)); + ITensor *output = get_backing_tensor(node.output(0)); + const float epsilon = node.epsilon(); + const ActivationLayerInfo fused_act = node.fused_activation(); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEBatchNormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Shape: " << input->info()->tensor_shape() + << " Epsilon: " << epsilon << " " + << (fused_act.enabled() ? to_string(fused_act.activation()) : "") + << " InPlace : " << is_in_place_operation(input, output) + << std::endl); + + return std::move(func); +} + +/** Create a backend convolution layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend convolution layer function + */ +std::unique_ptr create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *weights = get_backing_tensor(node.input(1)); + ITensor *biases = get_backing_tensor(node.input(2)); + ITensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const ConvolutionMethod conv_algorithm = node.convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::shared_ptr mm = get_memory_manager(ctx, Target::NEON); + std::unique_ptr func; + std::string func_name; + if(conv_algorithm == ConvolutionMethod::DIRECT) + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("NEDirectConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + else if(conv_algorithm == ConvolutionMethod::GEMM) + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("NEGEMMConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + else if(conv_algorithm == ConvolutionMethod::WINOGRAD) + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("NEWinogradLayer"), mm, + input, weights, biases, output, conv_info); + } + else + { + std::tie(func, func_name) = create_named_memory_managed_function(std::string("NEConvolutionLayer"), mm, + input, weights, biases, output, conv_info); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend layer depth concatenate function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth concatenate layer function + */ +std::unique_ptr create_depth_concatenate_layer(DepthConcatenateLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Return nullptr if depth concatenate is switched off + if(!node.is_enabled()) + { + return nullptr; + } + + // Extract IO and info + std::vector inputs; + for(unsigned int i = 0; i < node.num_inputs(); ++i) + { + inputs.push_back(get_backing_tensor(node.input(i))); + } + ITensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(inputs, output); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEDepthConcatenateLayer" + << " Data Type: " << output->info()->data_type() + << " Shape: " << output->info()->tensor_shape() + << " Num Inputs: " << inputs.size() + << std::endl); + + return std::move(func); +} + +/** Create a backend layer depth-wise convolution function + * + * @param[in] node Node to create the backend function for + * + * @return Backend depth-wise convolution layer function + */ +std::unique_ptr create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *weights = get_backing_tensor(node.input(1)); + ITensor *biases = get_backing_tensor(node.input(2)); + ITensor *output = get_backing_tensor(node.output(0)); + const PadStrideInfo conv_info = node.convolution_info(); + const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method(); + + // Create and configure function (we assume that functions have been validated before creation) + std::unique_ptr func; + std::string func_name; + if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3) + { + std::tie(func, func_name) = create_named_function(std::string("NEDepthwiseConvolutionLayer3x3"), + input, weights, biases, output, conv_info); + } + else + { + std::tie(func, func_name) = create_named_function(std::string("NEDepthwiseConvolutionLayer"), + input, weights, biases, output, conv_info); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + return func; +} + +/** Create a backend element-wise operation layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend element-wise operation layer function + */ +std::unique_ptr create_eltwise_layer(EltwiseLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input1 = get_backing_tensor(node.input(0)); + ITensor *input2 = get_backing_tensor(node.input(1)); + ITensor *output = get_backing_tensor(node.output(0)); + const EltwiseOperation eltwise_op = node.eltwise_operation(); + ARM_COMPUTE_ERROR_ON(input1 == nullptr); + ARM_COMPUTE_ERROR_ON(input2 == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + std::unique_ptr func = nullptr; + std::string func_name; + if(eltwise_op == EltwiseOperation::ADD) + { + std::tie(func, func_name) = create_named_function(std::string("NEArithmeticAddition"), + input1, input2, output, ConvertPolicy::SATURATE); + } + else if(eltwise_op == EltwiseOperation::SUB) + { + std::tie(func, func_name) = create_named_function(std::string("NEArithmeticSubtraction"), + input1, input2, output, ConvertPolicy::SATURATE); + } + else if(eltwise_op == EltwiseOperation::MUL) + { + std::tie(func, func_name) = create_named_function(std::string("NEPixelWiseMultiplication"), + input1, input2, output, 1.f, + ConvertPolicy::SATURATE, RoundingPolicy::TO_NEAREST_EVEN); + } + else + { + ARM_COMPUTE_ERROR("Unsupported element-wise operation!"); + } + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name + << " Data Type: " << input1->info()->data_type() + << " Shape : " << input1->info()->tensor_shape() + << std::endl); + + return func; +} + +/** Create a backend flatten layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend flatten layer function + */ +std::unique_ptr create_flatten_layer(FlattenLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON FlattenLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEFlattenLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend fully connected layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend fully connected layer function + */ +std::unique_ptr create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *weights = get_backing_tensor(node.input(1)); + ITensor *biases = get_backing_tensor(node.input(2)); + ITensor *output = get_backing_tensor(node.output(0)); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::NEON)); + func->configure(input, weights, biases, output); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(weights == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEFullyConnectedLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Weights shape: " << weights->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend normalization layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend normalization layer function + */ +std::unique_ptr create_normalization_layer(NormalizationLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + const NormalizationLayerInfo norm_info = node.normalization_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::NEON)); + func->configure(input, output, norm_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NENormalizationLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Normalization info: " << norm_info.type() + << std::endl); + + return std::move(func); +} + +/** Create a backend pooling layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend pooling layer function + */ +std::unique_ptr create_pooling_layer(PoolingLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + const PoolingLayerInfo pool_info = node.pooling_info(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output, pool_info); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEPoolingLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << " Pooling info: " << pool_info.pool_type() + << std::endl); + + return std::move(func); +} + +/** Create a backend reshape layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend reshape layer function + */ +std::unique_ptr create_reshape_layer(ReshapeLayerNode &node) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ReshapeLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(); + func->configure(input, output); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEReshapeLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} + +/** Create a backend softmax layer function + * + * @param[in] node Node to create the backend function for + * + * @return Backend softmax layer function + */ +std::unique_ptr create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx) +{ + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); + ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1); + ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); + + // Extract IO and info + ITensor *input = get_backing_tensor(node.input(0)); + ITensor *output = get_backing_tensor(node.output(0)); + const float beta = node.beta(); + ARM_COMPUTE_ERROR_ON(input == nullptr); + ARM_COMPUTE_ERROR_ON(output == nullptr); + + // Create and configure function + auto func = support::cpp14::make_unique(get_memory_manager(ctx, Target::NEON)); + func->configure(input, output, beta); + + // Log info + ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NESoftmaxLayer" + << " Data Type: " << input->info()->data_type() + << " Input shape: " << input->info()->tensor_shape() + << " Output shape: " << output->info()->tensor_shape() + << std::endl); + + return std::move(func); +} +} // namespace + +std::unique_ptr NEFunctionFactory::create(INode *node, GraphContext &ctx) +{ + if(node == nullptr) + { + return nullptr; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ActivationLayer: + return create_activation_layer(*polymorphic_downcast(node)); + case NodeType::BatchNormalizationLayer: + return create_batch_normalization_layer(*polymorphic_downcast(node)); + case NodeType::ConvolutionLayer: + return create_convolution_layer(*polymorphic_downcast(node), ctx); + case NodeType::DepthConcatenateLayer: + return create_depth_concatenate_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return create_depthwise_convolution_layer(*polymorphic_downcast(node)); + case NodeType::EltwiseLayer: + return create_eltwise_layer(*polymorphic_downcast(node)); + case NodeType::FlattenLayer: + return create_flatten_layer(*polymorphic_downcast(node)); + case NodeType::FullyConnectedLayer: + return create_fully_connected_layer(*polymorphic_downcast(node), ctx); + case NodeType::NormalizationLayer: + return create_normalization_layer(*polymorphic_downcast(node), ctx); + case NodeType::PoolingLayer: + return create_pooling_layer(*polymorphic_downcast(node)); + case NodeType::ReshapeLayer: + return create_reshape_layer(*polymorphic_downcast(node)); + case NodeType::SoftmaxLayer: + return create_softmax_layer(*polymorphic_downcast(node), ctx); + default: + return nullptr; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/NEON/NENodeValidator.cpp b/src/graph/backends/NEON/NENodeValidator.cpp new file mode 100644 index 0000000000..074f03580f --- /dev/null +++ b/src/graph/backends/NEON/NENodeValidator.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/NEON/NENodeValidator.h" + +#include "arm_compute/graph/backends/ValidateHelpers.h" +#include "arm_compute/graph/nodes/Nodes.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/runtime/NEON/NEFunctions.h" + +using namespace arm_compute::utils::cast; + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +Status NENodeValidator::validate(INode *node) +{ + if(node == nullptr) + { + return Status{}; + } + + NodeType type = node->type(); + switch(type) + { + case NodeType::ConvolutionLayer: + return detail::validate_convolution_layer(*polymorphic_downcast(node)); + case NodeType::DepthwiseConvolutionLayer: + return detail::validate_depthwise_convolution_layer(*polymorphic_downcast(node)); + + default: + return Status{}; + } +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/NEON/NESubTensorHandle.cpp b/src/graph/backends/NEON/NESubTensorHandle.cpp new file mode 100644 index 0000000000..c48ba6b9d6 --- /dev/null +++ b/src/graph/backends/NEON/NESubTensorHandle.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/NEON/NESubTensorHandle.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +NESubTensorHandle::NESubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent) + : _sub_tensor() +{ + ARM_COMPUTE_ERROR_ON(!parent_handle); + _sub_tensor = arm_compute::SubTensor(&parent_handle->tensor(), shape, coords, extend_parent); +} + +void NESubTensorHandle::allocate() +{ + // noop +} + +const arm_compute::ITensor &NESubTensorHandle::tensor() const +{ + return _sub_tensor; +} + +arm_compute::ITensor &NESubTensorHandle::tensor() +{ + return _sub_tensor; +} + +void NESubTensorHandle::map(bool blocking) +{ + ARM_COMPUTE_UNUSED(blocking); +} + +void NESubTensorHandle::unmap() +{ + // noop +} + +void NESubTensorHandle::release_if_unused() +{ + // noop +} + +bool NESubTensorHandle::is_subtensor() const +{ + return true; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/backends/NEON/NETensorHandle.cpp b/src/graph/backends/NEON/NETensorHandle.cpp new file mode 100644 index 0000000000..8508ac9511 --- /dev/null +++ b/src/graph/backends/NEON/NETensorHandle.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/backends/NEON/NETensorHandle.h" + +namespace arm_compute +{ +namespace graph +{ +namespace backends +{ +NETensorHandle::NETensorHandle(const ITensorInfo &info) + : _tensor() +{ + _tensor.allocator()->init(info); +} + +void NETensorHandle::allocate() +{ + _tensor.allocator()->allocate(); +} + +const arm_compute::ITensor &NETensorHandle::tensor() const +{ + return _tensor; +} + +arm_compute::ITensor &NETensorHandle::tensor() +{ + return _tensor; +} + +void NETensorHandle::map(bool blocking) +{ + ARM_COMPUTE_UNUSED(blocking); +} + +void NETensorHandle::unmap() +{ +} + +void NETensorHandle::release_if_unused() +{ + // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used + if(!_tensor.is_used()) + { + _tensor.allocator()->free(); + } +} + +bool NETensorHandle::is_subtensor() const +{ + return false; +} +} // namespace backends +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/detail/ExecutionHelpers.cpp b/src/graph/detail/ExecutionHelpers.cpp new file mode 100644 index 0000000000..5a50728164 --- /dev/null +++ b/src/graph/detail/ExecutionHelpers.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/detail/ExecutionHelpers.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/GraphContext.h" +#include "arm_compute/graph/GraphManager.h" +#include "arm_compute/graph/Tensor.h" +#include "arm_compute/graph/backends/BackendRegistry.h" + +namespace arm_compute +{ +namespace graph +{ +namespace detail +{ +void default_initialize_backends() +{ + for(const auto &backend : backends::BackendRegistry::get().backends()) + { + backend.second->initialize_backend(); + } +} + +void configure_all_tensors(Graph &g) +{ + auto &tensors = g.tensors(); + + for(auto &tensor : tensors) + { + if(tensor) + { + Target target = tensor->desc().target; + auto backend = backends::BackendRegistry::get().find_backend(target); + ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!"); + auto handle = backend->create_tensor(*tensor); + ARM_COMPUTE_ERROR_ON_MSG(!backend, "Couldn't create backend handle!"); + tensor->set_handle(std::move(handle)); + } + } +} + +void allocate_all_tensors(Graph &g) +{ + auto &tensors = g.tensors(); + + for(auto &tensor : tensors) + { + if(tensor && !tensor->bound_edges().empty()) + { + ARM_COMPUTE_ERROR_ON_MSG(!tensor->handle(), "Tensor handle is not configured!"); + tensor->handle()->allocate(); + } + } +} + +void validate_all_nodes(Graph &g) +{ + auto &nodes = g.nodes(); + + // Create tasks + for(auto &node : nodes) + { + if(node != nullptr) + { + Target assigned_target = node->assigned_target(); + auto backend = backends::BackendRegistry::get().find_backend(assigned_target); + ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!"); + Status status = backend->validate_node(*node); + ARM_COMPUTE_ERROR_ON_MSG(!bool(status), status.error_description().c_str()); + } + } +} + +ExecutionWorkload configure_all_nodes(Graph &g, GraphContext &ctx) +{ + ExecutionWorkload workload; + auto &nodes = g.nodes(); + + // Create tasks + for(auto &node : nodes) + { + if(node != nullptr) + { + Target assigned_target = node->assigned_target(); + auto backend = backends::BackendRegistry::get().find_backend(assigned_target); + ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!"); + auto func = backend->configure_node(*node, ctx); + if(func != nullptr) + { + ExecutionTask task; + task.task = std::move(func); + task.node = node.get(); + workload.tasks.push_back(std::move(task)); + } + } + } + + // Add inputs and outputs + for(auto &node : nodes) + { + if(node != nullptr && node->type() == NodeType::Input) + { + workload.inputs.push_back(node->output(0)); + } + + if(node != nullptr && node->type() == NodeType::Output) + { + workload.outputs.push_back(node->input(0)); + continue; + } + } + + return workload; +} + +void release_unused_tensors(Graph &g) +{ + for(auto &tensor : g.tensors()) + { + if(tensor != nullptr && tensor->handle() != nullptr) + { + tensor->handle()->release_if_unused(); + } + } +} + +void call_tensor_accessor(Tensor *tensor) +{ + ARM_COMPUTE_ERROR_ON(!tensor); + tensor->call_accessor(); +} + +void call_all_const_node_accessors(Graph &g) +{ + auto &nodes = g.nodes(); + + for(auto &node : nodes) + { + if(node != nullptr && node->type() == NodeType::Const) + { + call_tensor_accessor(node->output(0)); + } + } +} + +void call_all_input_node_accessors(ExecutionWorkload &workload) +{ + for(auto &input : workload.inputs) + { + if(input != nullptr) + { + input->call_accessor(); + } + } +} + +void call_all_tasks(ExecutionWorkload &workload) +{ + for(auto &task : workload.tasks) + { + task(); + } +} + +void call_all_output_node_accessors(ExecutionWorkload &workload) +{ + for(auto &output : workload.outputs) + { + if(output != nullptr) + { + output->call_accessor(); + } + } +} +} // namespace detail +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/frontend/Stream.cpp b/src/graph/frontend/Stream.cpp new file mode 100644 index 0000000000..96a166c79c --- /dev/null +++ b/src/graph/frontend/Stream.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/frontend/Stream.h" + +#include "arm_compute/graph/Utils.h" +#include "arm_compute/graph/frontend/ILayer.h" + +namespace arm_compute +{ +namespace graph +{ +namespace frontend +{ +Stream::Stream(size_t id, std::string name) + : _manager(), _ctx(), _g(id, std::move(name)) +{ +} + +void Stream::finalize(Target target, const GraphConfig &config) +{ + PassManager pm = create_default_pass_manager(target); + _ctx.set_config(config); + _manager.finalize_graph(_g, _ctx, pm, target); +} + +void Stream::run() +{ + _manager.execute_graph(_g); +} + +void Stream::add_layer(ILayer &layer) +{ + auto nid = layer.create_layer(*this); + _tail_node = nid; +} + +const Graph &Stream::graph() const +{ + return _g; +} + +Graph &Stream::graph() +{ + return _g; +} +} // namespace frontend +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/frontend/SubStream.cpp b/src/graph/frontend/SubStream.cpp new file mode 100644 index 0000000000..e8bd23a557 --- /dev/null +++ b/src/graph/frontend/SubStream.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/frontend/SubStream.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/frontend/ILayer.h" + +namespace arm_compute +{ +namespace graph +{ +namespace frontend +{ +SubStream::SubStream(IStream &s) + : _s(s) +{ + _hints = s.hints(); + _tail_node = s.tail_node(); +} + +void SubStream::add_layer(ILayer &layer) +{ + auto nid = layer.create_layer(*this); + _tail_node = nid; +} + +const Graph &SubStream::graph() const +{ + return _s.graph(); +} + +Graph &SubStream::graph() +{ + return _s.graph(); +} +} // namespace frontend +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/mutators/DepthConcatSubTensorMutator.cpp b/src/graph/mutators/DepthConcatSubTensorMutator.cpp new file mode 100644 index 0000000000..c56f4c5106 --- /dev/null +++ b/src/graph/mutators/DepthConcatSubTensorMutator.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/mutators/DepthConcatSubTensorMutator.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/backends/BackendRegistry.h" +#include "arm_compute/graph/nodes/DepthConcatenateLayerNode.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/core/utils/misc/Iterable.h" + +namespace arm_compute +{ +namespace graph +{ +const char *DepthConcatSubTensorMutator::name() +{ + return "DepthConcatSubTensorMutator"; +} + +void DepthConcatSubTensorMutator::mutate(Graph &g) +{ + // Should be in reverse order of execution + for(auto &node : arm_compute::utils::iterable::reverse_iterate(g.nodes())) + { + if(node && node->type() == NodeType::DepthConcatenateLayer && node->output(0) != nullptr) + { + // Get output tensor + auto output_tensor = node->output(0); + + // Check that all tensor have the same target and valid inputs + bool is_valid = std::all_of(node->input_edges().cbegin(), node->input_edges().cend(), + [&](const EdgeID & eid) + { + return (g.edge(eid) != nullptr) && (g.edge(eid)->tensor() != nullptr) && (g.edge(eid)->tensor()->desc().target == output_tensor->desc().target); + }); + + // Create subtensors + if(is_valid && backends::BackendRegistry::get().find_backend(output_tensor->desc().target) != nullptr) + { + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Using sub-tensors for the node with ID : " + << node->id() << " and name : " << node->name() << std::endl); + // Create sub-tensor handles + unsigned depth = 0; + for(unsigned int i = 0; i < node->input_edges().size(); ++i) + { + auto input_tensor = node->input(i); + const auto input_shape = input_tensor->desc().shape; + + auto backend = backends::BackendRegistry::get().find_backend(input_tensor->desc().target); + auto handle = backend->create_subtensor(output_tensor->handle(), input_shape, Coordinates(0, 0, depth), false); + input_tensor->set_handle(std::move(handle)); + + depth += input_shape.z(); + } + + auto *dc_node = arm_compute::utils::cast::polymorphic_downcast(node.get()); + dc_node->set_enabled(false); + } + } + } +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/mutators/InPlaceOperationMutator.cpp b/src/graph/mutators/InPlaceOperationMutator.cpp new file mode 100644 index 0000000000..bd3f098965 --- /dev/null +++ b/src/graph/mutators/InPlaceOperationMutator.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/mutators/InPlaceOperationMutator.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Logger.h" + +namespace arm_compute +{ +namespace graph +{ +const char *InPlaceOperationMutator::name() +{ + return "InPlaceOperationMutator"; +} + +void InPlaceOperationMutator::mutate(Graph &g) +{ + std::set in_place_nodes = { NodeType::BatchNormalizationLayer, NodeType::ActivationLayer }; + + // Not interested in the order of nodes + for(auto &node : g.nodes()) + { + if(node && in_place_nodes.find(node->type()) != std::end(in_place_nodes)) + { + // Get input edge + Edge *input_edge = node->input_edge(0); + + // Check if parent has a single output if yes then force in place calculation else not + if((input_edge != nullptr) && (input_edge->producer() != nullptr) && (input_edge->producer()->output_edges().size() == 1)) + { + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Switching to in-place computation for the node with ID : " + << node->id() << " and name : " << node->name() << std::endl); + // Update output + auto tensor = input_edge->tensor(); + node->set_output_tensor(tensor->id(), 0); + } + } + } +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/mutators/NodeFusionMutator.cpp b/src/graph/mutators/NodeFusionMutator.cpp new file mode 100644 index 0000000000..2e893c2e07 --- /dev/null +++ b/src/graph/mutators/NodeFusionMutator.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/mutators/NodeFusionMutator.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/nodes/Nodes.h" + +#include "arm_compute/core/utils/misc/Cast.h" + +namespace arm_compute +{ +namespace graph +{ +namespace detail +{ +void fuse_batch_norm_with_activation(Graph &g) +{ + // Not interested in the order of nodes + for(auto &node : g.nodes()) + { + // Check if the node is batch norm and not a branching node + if(node && node->type() == NodeType::BatchNormalizationLayer && node->output_edges().size() == 1) + { + auto output_edge_id = *node->output_edges().begin(); + auto output_edge = g.edge(output_edge_id); + // Check if following node is an activation layer node + if((output_edge != nullptr) && (output_edge->consumer() != nullptr) && (output_edge->consumer()->type() == NodeType::ActivationLayer)) + { + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing Batch Normalization node with ID : " << output_edge->producer_id() + << " with Activation Layer node with ID : " << output_edge->consumer_id() << std::endl); + + auto *bn_node = arm_compute::utils::cast::polymorphic_downcast(output_edge->producer()); + auto *act_node = arm_compute::utils::cast::polymorphic_downcast(output_edge->consumer()); + + // Get driving nodes of activation node + std::vector act_driving_nodes; + for(auto &act_output_edge_id : act_node->output_edges()) + { + auto act_output_edge = g.edge(act_output_edge_id); + if(act_output_edge != nullptr) + { + ARM_COMPUTE_ERROR_ON(act_output_edge->consumer() == nullptr); + act_driving_nodes.push_back({ act_output_edge->consumer_id(), act_output_edge->consumer_idx() }); + } + } + + // Set activation info to batch normalization + bn_node->set_fused_activation(act_node->activation_info()); + + // Remove activation node + g.remove_node(act_node->id()); + + // Update batch normalization node outputs + for(auto &driving_node : act_driving_nodes) + { + g.add_connection(bn_node->id(), 0, driving_node.node_id, driving_node.index); + } + } + } + } +} +} // namespace detail + +const char *NodeFusionMutator::name() +{ + return "NodeFusionMutator"; +} + +void NodeFusionMutator::mutate(Graph &g) +{ + detail::fuse_batch_norm_with_activation(g); +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/mutators/SplitLayerSubTensorMutator.cpp b/src/graph/mutators/SplitLayerSubTensorMutator.cpp new file mode 100644 index 0000000000..179a6c35fb --- /dev/null +++ b/src/graph/mutators/SplitLayerSubTensorMutator.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/mutators/SplitLayerSubTensorMutator.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Logger.h" +#include "arm_compute/graph/backends/BackendRegistry.h" +#include "arm_compute/graph/nodes/SplitLayerNode.h" + +#include "arm_compute/core/utils/misc/Cast.h" +#include "arm_compute/core/utils/misc/Iterable.h" + +namespace arm_compute +{ +namespace graph +{ +const char *SplitLayerSubTensorMutator::name() +{ + return "SplitLayerSubTensorMutator"; +} + +void SplitLayerSubTensorMutator::mutate(Graph &g) +{ + // Should be in reverse order of execution + for(auto &node : arm_compute::utils::iterable::reverse_iterate(g.nodes())) + { + if(node && node->type() == NodeType::SplitLayer && node->input(0) != nullptr) + { + // Get output tensor + Tensor *input_tensor = node->input(0); + + // Check that all tensor have the same target and are valid + bool is_valid = std::all_of(node->outputs().cbegin(), node->outputs().cend(), + [&](const TensorID & tid) + { + return (g.tensor(tid) != nullptr) && (g.tensor(tid)->desc().target == input_tensor->desc().target); + }); + + // Create subtensors + if(is_valid && backends::BackendRegistry::get().find_backend(input_tensor->desc().target) != nullptr) + { + ARM_COMPUTE_LOG_GRAPH_VERBOSE("Using sub-tensors for the node with ID : " + << node->id() << " and name : " << node->name() << std::endl); + + auto *split_node = arm_compute::utils::cast::polymorphic_downcast(node.get()); + + const unsigned int axis = split_node->axis(); + const unsigned int num_splits = split_node->num_splits(); + const bool extend_parent = (axis < 2); + + // Create sub-tensor handles + for(unsigned int i = 0; i < node->outputs().size(); ++i) + { + Tensor *output_tensor = node->output(i); + const TensorShape output_shape = output_tensor->desc().shape; + Coordinates coords; + std::tie(std::ignore, coords) = SplitLayerNode::compute_output_shape(input_tensor->desc().shape, num_splits, axis, i); + + backends::IDeviceBackend *backend = backends::BackendRegistry::get().find_backend(output_tensor->desc().target); + std::unique_ptr handle = backend->create_subtensor(input_tensor->handle(), output_shape, coords, extend_parent); + output_tensor->set_handle(std::move(handle)); + } + } + } + } +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/nodes/ActivationLayer.cpp b/src/graph/nodes/ActivationLayer.cpp deleted file mode 100644 index 546c42a1e5..0000000000 --- a/src/graph/nodes/ActivationLayer.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/ActivationLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -ActivationLayer::ActivationLayer(const ActivationLayerInfo activation_info) - : _activation_info(activation_info) -{ - set_supports_in_place(true); -} - -std::unique_ptr ActivationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::ActivationLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - node_ctx.add_parameter("ActivationLayerInfo", _activation_info); - - // Get function - return OperationRegistry::get().find_operation(OperationType::ActivationLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/ActivationLayerNode.cpp b/src/graph/nodes/ActivationLayerNode.cpp new file mode 100644 index 0000000000..9996d2ce3f --- /dev/null +++ b/src/graph/nodes/ActivationLayerNode.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/ActivationLayerNode.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +ActivationLayerNode::ActivationLayerNode(ActivationLayerInfo info) + : _info(info) +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +ActivationLayerInfo ActivationLayerNode::activation_info() const +{ + return _info; +} + +bool ActivationLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor ActivationLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + return src->desc(); +} + +Status ActivationLayerNode::validate() +{ + return Status{}; +} + +NodeType ActivationLayerNode::type() const +{ + return NodeType::ActivationLayer; +} + +void ActivationLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/BatchNormalizationLayer.cpp b/src/graph/nodes/BatchNormalizationLayer.cpp deleted file mode 100644 index 24287ac61a..0000000000 --- a/src/graph/nodes/BatchNormalizationLayer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/BatchNormalizationLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -std::unique_ptr BatchNormalizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - unsigned int batch_norm_size = in->info()->dimension(2); - if(_mean.tensor() == nullptr) - { - _mean.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); - } - if(_var.tensor() == nullptr) - { - _var.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); - } - if(_beta.tensor() == nullptr) - { - _beta.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); - } - if(_gamma.tensor() == nullptr) - { - _gamma.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); - } - - bool mean_is_loaded = _mean.tensor() != nullptr; - bool var_is_loaded = _var.tensor() != nullptr; - bool gamma_is_loaded = _gamma.tensor() != nullptr; - bool beta_is_loaded = _beta.tensor() != nullptr; - - // Set mean, var, gamma and beta target - _mean.set_target(_target_hint); - _var.set_target(_target_hint); - _gamma.set_target(_target_hint); - _beta.set_target(_target_hint); - - // Create node context - NodeContext node_ctx(OperationType::BatchNormalizationLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_input(_mean.tensor()); - node_ctx.add_input(_var.tensor()); - node_ctx.add_input(_beta.tensor()); - node_ctx.add_input(_gamma.tensor()); - node_ctx.add_output(out); - node_ctx.add_parameter("epsilon", _epsilon); - node_ctx.add_parameter("act_info", _act_info); - - // Configure operation - auto func = OperationRegistry::get().find_operation(OperationType::BatchNormalizationLayer, _target_hint)->configure(node_ctx); - - // Fill tensors - if(!mean_is_loaded) - { - _mean.allocate_and_fill_if_needed(); - } - if(!var_is_loaded) - { - _var.allocate_and_fill_if_needed(); - } - if(!gamma_is_loaded) - { - _gamma.allocate_and_fill_if_needed(); - } - if(!beta_is_loaded) - { - _beta.allocate_and_fill_if_needed(); - } - - // Get function - return func; -} \ No newline at end of file diff --git a/src/graph/nodes/BatchNormalizationLayerNode.cpp b/src/graph/nodes/BatchNormalizationLayerNode.cpp new file mode 100644 index 0000000000..f7b041c828 --- /dev/null +++ b/src/graph/nodes/BatchNormalizationLayerNode.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/BatchNormalizationLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +BatchNormalizationLayerNode::BatchNormalizationLayerNode(float epsilon, ActivationLayerInfo fused_activation) + : _epsilon(epsilon), _fused_activation(fused_activation) +{ + _input_edges.resize(5, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +float BatchNormalizationLayerNode::epsilon() const +{ + return _epsilon; +} + +ActivationLayerInfo BatchNormalizationLayerNode::fused_activation() const +{ + return _fused_activation; +} + +void BatchNormalizationLayerNode::set_fused_activation(ActivationLayerInfo fused_activation) +{ + _fused_activation = fused_activation; +} + +bool BatchNormalizationLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor BatchNormalizationLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + return src->desc(); +} + +Status BatchNormalizationLayerNode::validate() +{ + return Status{}; +} + +NodeType BatchNormalizationLayerNode::type() const +{ + return NodeType::BatchNormalizationLayer; +} + +void BatchNormalizationLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/BranchLayer.cpp b/src/graph/nodes/BranchLayer.cpp deleted file mode 100644 index 7a20a565b8..0000000000 --- a/src/graph/nodes/BranchLayer.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/BranchLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/Graph.h" -#include "arm_compute/graph/SubGraph.h" -#include "arm_compute/graph/Tensor.h" -#include "arm_compute/runtime/IFunction.h" -#include "support/ToolchainSupport.h" -#include "utils/TypePrinter.h" - -#include -#include -#include - -using namespace arm_compute::graph; - -/** Branch function */ -class BranchFunction final : public arm_compute::IFunction -{ -public: - /** Default Constructor */ - BranchFunction() - : _graphs() - { - } - /** Registers graph to be executed by the branch function - * - * @param[in] graph Graph to register - */ - void register_graph(std::unique_ptr graph) - { - _graphs.push_back(std::move(graph)); - } - // Inherited methods overriden: - void run() override - { - for(auto &g : _graphs) - { - ARM_COMPUTE_ERROR_ON(g.get() == nullptr); - g->run(); - } - } - -private: - std::vector> _graphs; -}; - -std::unique_ptr BranchLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON(_branch_merge_method != BranchMergeMethod::DEPTH_CONCATENATE); - ARM_COMPUTE_UNUSED(_branch_merge_method); - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - // Create branch function - auto func = arm_compute::support::cpp14::make_unique(); - - // Track output depth - int depth = 0; - - // Constuct all sub-graphs given the input/output - for(auto &sg : _sub_graphs) - { - ARM_COMPUTE_ERROR_ON(sg.get() == nullptr); - - // IO buffers - std::unique_ptr in; - std::unique_ptr out; - SubTensor *out_sub_tensor = nullptr; - - // Create input sub-tensor - if(!sg->has_input()) - { - ARM_COMPUTE_ERROR_ON(dynamic_cast(input) == nullptr); - in = arm_compute::support::cpp14::make_unique(*dynamic_cast(input), - input->tensor()->info()->tensor_shape(), - Coordinates()); - } - - // Create output sub-tensor - if(!sg->has_output()) - { - ARM_COMPUTE_ERROR_ON((dynamic_cast(output) == nullptr) && (dynamic_cast(output) == nullptr)); - - out = arm_compute::support::cpp14::make_unique(output->tensor(), - TensorShape(), - Coordinates(0, 0, depth), - output->target(), - true); - out_sub_tensor = dynamic_cast(out.get()); - } - - // Construct sub_graph - auto g = sg->construct(ctx, std::move(in), std::move(out)); - - // Register graph to function - func->register_graph(std::move(g)); - - // Update and track depth - if(out_sub_tensor != nullptr) - { - ARM_COMPUTE_ERROR_ON(out_sub_tensor->tensor() == nullptr); - depth += out_sub_tensor->tensor()->info()->tensor_shape()[2]; - } - } - - return std::move(func); -} \ No newline at end of file diff --git a/src/graph/nodes/ConstNode.cpp b/src/graph/nodes/ConstNode.cpp new file mode 100644 index 0000000000..631971c98f --- /dev/null +++ b/src/graph/nodes/ConstNode.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/ConstNode.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +ConstNode::ConstNode(TensorDescriptor desc) + : _desc(desc) +{ + _outputs.resize(1, NullTensorID); +} + +bool ConstNode::forward_descriptors() +{ + if(output_id(0) != NullTensorID) + { + Tensor *t = output(0); + ARM_COMPUTE_ERROR_ON(t == nullptr); + t->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor ConstNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + return _desc; +} + +Status ConstNode::validate() +{ + return Status{}; +} + +NodeType ConstNode::type() const +{ + return NodeType::Const; +} + +void ConstNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/nodes/ConvolutionLayer.cpp b/src/graph/nodes/ConvolutionLayer.cpp deleted file mode 100644 index 5b3a84a4ad..0000000000 --- a/src/graph/nodes/ConvolutionLayer.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/ConvolutionLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h" -#include "arm_compute/runtime/CL/functions/CLDirectConvolutionLayer.h" -#include "arm_compute/runtime/CL/functions/CLWinogradConvolutionLayer.h" -#include "arm_compute/runtime/IFunction.h" -#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h" -#include "arm_compute/runtime/NEON/functions/NEDirectConvolutionLayer.h" -#include "arm_compute/runtime/NEON/functions/NEWinogradLayer.h" -#include "support/ToolchainSupport.h" -#include "utils/GraphTypePrinter.h" -#include "utils/TypePrinter.h" - -#include -#include - -using namespace arm_compute::graph; - -namespace -{ -/** Calculates the output shaped of the convolution layer - * - * @param[in] input_shape Input tensor shape - * @param[in] weights_shape Weights shape - * @param[in] conv_info Convolution information (padding, stride, etc.) - * - * @return The expected output tensor shape - */ -TensorShape calculate_convolution_layer_output_shape(const TensorShape &input_shape, const TensorShape &weights_shape, const PadStrideInfo &conv_info) -{ - unsigned int output_width = 0; - unsigned int output_height = 0; - - // Get output width and height - std::tie(output_width, output_height) = arm_compute::scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), conv_info); - - // Create output shape - TensorShape output_shape = input_shape; - output_shape.set(0, output_width); - output_shape.set(1, output_height); - output_shape.set(2, weights_shape[3]); - - return output_shape; -} - -// Instantiate GEMM based convolution layer -template -std::unique_ptr instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output, - const PadStrideInfo &conv_info, const WeightsInfo &weights_info) -{ - auto conv = arm_compute::support::cpp14::make_unique(); - conv->configure( - dynamic_cast(input), - dynamic_cast(weights), - dynamic_cast(biases), - dynamic_cast(output), - conv_info, weights_info); - return std::move(conv); -} - -// Instantiate direct convolution layer -template -std::unique_ptr instantiate_direct_function(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output, - const PadStrideInfo &conv_info) -{ - auto conv = arm_compute::support::cpp14::make_unique(); - conv->configure( - dynamic_cast(input), - dynamic_cast(weights), - dynamic_cast(biases), - dynamic_cast(output), - conv_info); - return std::move(conv); -} - -template -std::unique_ptr instantiate(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output, - const PadStrideInfo &conv_info, const WeightsInfo &weights_info, - ConvolutionMethodHint conv_method); - -template <> -std::unique_ptr instantiate(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output, - const PadStrideInfo &conv_info, - const WeightsInfo &weights_info, - ConvolutionMethodHint conv_method) -{ - if((conv_method == ConvolutionMethodHint::WINOGRAD) - && arm_compute::CLWinogradConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLWinogradConvolutionLayer"); - return instantiate_direct_function(input, weights, biases, output, conv_info); - } - else if((conv_method == ConvolutionMethodHint::DIRECT) - && arm_compute::CLDirectConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDirectConvolutionLayer"); - return instantiate_direct_function(input, weights, biases, output, conv_info); - } - else - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLConvolutionLayer"); - return instantiate_function(input, weights, biases, output, conv_info, weights_info); - } -} - -template <> -std::unique_ptr instantiate(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output, - const PadStrideInfo &conv_info, - const WeightsInfo &weights_info, - ConvolutionMethodHint conv_method) -{ - const unsigned int kernel_size_x = weights->info()->tensor_shape().x(); - const unsigned int kernel_size_y = weights->info()->tensor_shape().y(); - const unsigned int conv_stride_x = conv_info.stride().first; - const unsigned int conv_stride_y = conv_info.stride().second; - - bool is_square_kernel = (kernel_size_x == kernel_size_y); - bool has_same_stride = (conv_stride_x == conv_stride_y); - - // TODO (COMPID-765) : Winograd should have a validate function - if(conv_method == ConvolutionMethodHint::WINOGRAD && is_square_kernel && ((kernel_size_x == 3) || (kernel_size_x == 5)) && has_same_stride && (conv_info.stride().first == 1)) - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEWinogradConvolutionLayer"); - return instantiate_direct_function(input, weights, biases, output, conv_info); - } - else if((conv_method == ConvolutionMethodHint::DIRECT) - && arm_compute::NEDirectConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDirectConvolutionLayer"); - return instantiate_direct_function(input, weights, biases, output, conv_info); - } - else - { - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEConvolutionLayer"); - return instantiate_function(input, weights, biases, output, conv_info, weights_info); - } -} -} // namespace - -/** Grouped Convolution function */ -class GroupedConvolutionFunction final : public arm_compute::IFunction -{ -public: - /** Default Constructor */ - GroupedConvolutionFunction() = default; - /** Default Destructor */ - ~GroupedConvolutionFunction() final = default; - /** Prevent instances from being copy constructed */ - GroupedConvolutionFunction(const GroupedConvolutionFunction &) = delete; - /** Prevent instances from being copy assigned */ - GroupedConvolutionFunction &operator=(const GroupedConvolutionFunction &) = delete; - /** Allow instances to be move constructed */ - GroupedConvolutionFunction(GroupedConvolutionFunction &&) noexcept = default; - /** Allow instances to be move assigned */ - GroupedConvolutionFunction &operator=(GroupedConvolutionFunction &&) noexcept = default; - /** Adds a convolution - * - * @param convolution Convolution function to add - */ - void add_convolution_function(std::unique_ptr convolution) // NOLINT - { - _convolutions.emplace_back(std::move(convolution)); - } - - // Inherited methods overridden: - void run() override - { - for(auto &c : _convolutions) - { - c->run(); - } - } - -private: - std::vector> _convolutions{}; -}; - -std::unique_ptr ConvolutionLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - - // Set weights and biases info - if(_weights.tensor() == nullptr) - { - TensorInfo info = TensorInfo(TensorShape(_conv_width, _conv_height, in->info()->dimension(2) / _num_groups, _ofm), - in->info()->num_channels(), - in->info()->data_type(), - in->info()->fixed_point_position()); - info.set_quantization_info(_weights_quant_info); - _weights.set_info(std::move(info)); - } - if(_biases.has_accessor() && _biases.tensor() == nullptr) - { - DataType dt = in->info()->data_type(); - _biases.set_info(TensorInfo(TensorShape(_ofm), in->info()->num_channels(), is_data_type_quantized_asymmetric(dt) ? DataType::S32 : dt, in->info()->fixed_point_position())); - } - - std::unique_ptr func; - _target_hint = ctx.hints().target_hint(); - const ConvolutionMethodHint conv_method_hint = ctx.hints().convolution_method_hint(); - - // Check if the weights and biases are loaded - bool weights_are_loaded = _weights.tensor() != nullptr; - bool biases_are_loaded = _biases.has_accessor() ? _biases.tensor() != nullptr : true; - - // Set bias and weights target - _weights.set_target(_target_hint); - if(_biases.has_accessor()) - { - _biases.set_target(_target_hint); - } - - // Calculate output shape - TensorShape output_shape = calculate_convolution_layer_output_shape(in->info()->tensor_shape(), _weights.info().tensor_shape(), _conv_info); - - // Output auto inizialitation if not yet initialized - arm_compute::auto_init_if_empty(*out->info(), output_shape, 1, in->info()->data_type(), in->info()->fixed_point_position(), - (_out_quant_info.empty()) ? in->info()->quantization_info() : _out_quant_info); - - // Create appropriate convolution function - if(_num_groups == 1) - { - func = instantiate_convolution(in, out, conv_method_hint); - } - else - { - func = instantiate_grouped_convolution(in, out, conv_method_hint); - } - - // Fill weights - if(!weights_are_loaded) - { - _weights.allocate_and_fill_if_needed(); - } - // Fill biases - if(!biases_are_loaded) - { - _biases.allocate_and_fill_if_needed(); - } - - ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type() - << " Input Shape: " << in->info()->tensor_shape() - << " Weights shape: " << _weights.info().tensor_shape() - << " Biases Shape: " << _biases.info().tensor_shape() - << " Output Shape: " << out->info()->tensor_shape() - << " PadStrideInfo: " << _conv_info - << " Groups: " << _num_groups - << " WeightsInfo: " << _weights_info - << std::endl); - - return func; -} - -std::unique_ptr ConvolutionLayer::instantiate_convolution(ITensor *input, ITensor *output, ConvolutionMethodHint conv_method_hint) -{ - std::unique_ptr func; - if(_target_hint == TargetHint::OPENCL) - { - func = instantiate(input, _weights.tensor(), _biases.tensor(), output, _conv_info, _weights_info, conv_method_hint); - } - else - { - func = instantiate(input, _weights.tensor(), _biases.tensor(), output, _conv_info, _weights_info, conv_method_hint); - } - return func; -} - -std::unique_ptr ConvolutionLayer::instantiate_grouped_convolution(ITensor *input, ITensor *output, ConvolutionMethodHint conv_method_hint) -{ - // Get tensor shapes - TensorShape input_shape = input->info()->tensor_shape(); - TensorShape output_shape = output->info()->tensor_shape(); - TensorShape weights_shape = _weights.info().tensor_shape(); - TensorShape biases_shape = _biases.info().tensor_shape(); - - ARM_COMPUTE_ERROR_ON_MSG((input_shape.z() % _num_groups) != 0, "Input depth not multiple of the number of groups!"); - ARM_COMPUTE_ERROR_ON_MSG((output_shape.z() % _num_groups) != 0, "Output depth not multiple of the number of groups!"); - ARM_COMPUTE_ERROR_ON_MSG((weights_shape[3] % _num_groups) != 0, "Number of kernels not multiple of the number of groups!"); - ARM_COMPUTE_ERROR_ON_MSG((biases_shape.x() % _num_groups) != 0, "Biases not multiple of the number of groups!"); - - // Create a grouped convolution function - auto grouped_conv = arm_compute::support::cpp14::make_unique(); - - // Create sub-tensors vectors - _is = arm_compute::support::cpp14::make_unique(_num_groups); - _os = arm_compute::support::cpp14::make_unique(_num_groups); - _ws = arm_compute::support::cpp14::make_unique(_num_groups); - _bs = arm_compute::support::cpp14::make_unique(_num_groups); - - // Calculate sub-tensor splits - const int input_split = input_shape.z() / _num_groups; - const int output_split = output_shape.z() / _num_groups; - const int weights_split = weights_shape[3] / _num_groups; - const int biases_split = biases_shape.x() / _num_groups; - - // Calculate sub-tensor shapes - input_shape.set(2, input_split); - output_shape.set(2, output_split); - weights_shape.set(3, weights_split); - biases_shape.set(0, biases_split); - - // Configure sub-tensors - for(int i = 0; i < static_cast(_num_groups); ++i) - { - // Create convolution function - std::unique_ptr func; - - // Calculate sub-tensors starting coordinates - Coordinates input_coord(0, 0, input_split * i); - Coordinates output_coord(0, 0, output_split * i); - Coordinates weights_coord(0, 0, 0, weights_split * i); - Coordinates biases_coord(biases_split * i); - - // Create sub-tensors for input, output, weights and bias - auto hint_to_use = (_target_hint == TargetHint::OPENCL) ? TargetHint::OPENCL : TargetHint::NEON; - _is[i] = SubTensor(input, input_shape, input_coord, hint_to_use); - _os[i] = SubTensor(output, output_shape, output_coord, hint_to_use); - _ws[i] = SubTensor(_weights.tensor(), weights_shape, weights_coord, hint_to_use); - _bs[i] = SubTensor(_biases.tensor(), biases_shape, biases_coord, hint_to_use); - - // Instantiate convolution function - if(_target_hint == TargetHint::OPENCL) - { - func = instantiate(_is[i].tensor(), _ws[i].tensor(), _bs[i].tensor(), _os[i].tensor(), _conv_info, _weights_info, conv_method_hint); - } - else - { - func = instantiate(_is[i].tensor(), _ws[i].tensor(), _bs[i].tensor(), _os[i].tensor(), _conv_info, _weights_info, conv_method_hint); - } - - // Add convolution function to the list of convolutions for the grouped convolution - grouped_conv->add_convolution_function(std::move(func)); - } - - return std::move(grouped_conv); -} diff --git a/src/graph/nodes/ConvolutionLayerNode.cpp b/src/graph/nodes/ConvolutionLayerNode.cpp new file mode 100644 index 0000000000..461728487f --- /dev/null +++ b/src/graph/nodes/ConvolutionLayerNode.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/ConvolutionLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +ConvolutionLayerNode::ConvolutionLayerNode(PadStrideInfo info, ConvolutionMethod method) + : _info(std::move(info)), _method(method) +{ + _input_edges.resize(3, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +void ConvolutionLayerNode::set_convolution_method(ConvolutionMethod method) +{ + _method = method; +} + +ConvolutionMethod ConvolutionLayerNode::convolution_method() const +{ + return _method; +} + +PadStrideInfo ConvolutionLayerNode::convolution_info() const +{ + return _info; +} + +TensorShape ConvolutionLayerNode::compute_output_shape(TensorShape input_shape, TensorShape weights_shape, PadStrideInfo info) +{ + unsigned int output_width = 0; + unsigned int output_height = 0; + std::tie(output_width, output_height) = scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), info); + + TensorShape output_shape{ input_shape }; + output_shape.set(0, output_width); + output_shape.set(1, output_height); + output_shape.set(2, weights_shape[3]); + + return output_shape; +} + +bool ConvolutionLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (input_id(1) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor ConvolutionLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + const Tensor *src = input(0); + const Tensor *weights = input(1); + + ARM_COMPUTE_ERROR_ON(src == nullptr || weights == nullptr); + + TensorDescriptor output_info = src->desc(); + TensorShape output_shape = compute_output_shape(src->desc().shape, weights->desc().shape, _info); + output_info.shape = output_shape; + return output_info; +} + +Status ConvolutionLayerNode::validate() +{ + return Status{}; +} + +NodeType ConvolutionLayerNode::type() const +{ + return NodeType::ConvolutionLayer; +} + +void ConvolutionLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/DeQuantizationLayer.cpp b/src/graph/nodes/DeQuantizationLayer.cpp deleted file mode 100644 index af9ecee157..0000000000 --- a/src/graph/nodes/DeQuantizationLayer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/DequantizationLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" - -using namespace arm_compute::graph; - -std::unique_ptr DequantizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - _target_hint = ctx.hints().target_hint(); - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - - if(_min_max.tensor() == nullptr) - { - TensorShape shape = in->info()->tensor_shape(); - shape.set(Window::DimX, 2); - shape.remove_dimension(1); - shape.remove_dimension(1); - - _min_max.set_info(TensorInfo(shape, in->info()->num_channels(), DataType::F32)); - _min_max.set_target(_target_hint); - } - - bool minmax_is_loaded = _min_max.tensor() != nullptr; - - // Create node context - NodeContext node_ctx(OperationType::DequantizationLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(_min_max.tensor()); - node_ctx.add_output(out); - - // Fill min max - if(!minmax_is_loaded) - { - _min_max.allocate_and_fill_if_needed(); - } - - // Get function - return OperationRegistry::get().find_operation(OperationType::DequantizationLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/DepthConcatenateLayerNode.cpp b/src/graph/nodes/DepthConcatenateLayerNode.cpp new file mode 100644 index 0000000000..1c0539744f --- /dev/null +++ b/src/graph/nodes/DepthConcatenateLayerNode.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/DepthConcatenateLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +DepthConcatenateLayerNode::DepthConcatenateLayerNode(unsigned int total_nodes) + : _total_nodes(total_nodes), _is_enabled(true) +{ + _input_edges.resize(total_nodes, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +void DepthConcatenateLayerNode::set_enabled(bool is_enabled) +{ + _is_enabled = is_enabled; +} + +bool DepthConcatenateLayerNode::is_enabled() const +{ + return _is_enabled; +} + +TensorShape DepthConcatenateLayerNode::compute_output_shape(const std::vector &input_shapes) +{ + ARM_COMPUTE_ERROR_ON(input_shapes.size() == 0); + + TensorShape output_shape = input_shapes[0]; + + size_t max_x = 0; + size_t max_y = 0; + size_t depth = 0; + + for(const auto &shape : input_shapes) + { + max_x = std::max(shape.x(), max_x); + max_y = std::max(shape.y(), max_y); + depth += shape.z(); + } + + output_shape.set(0, max_x); + output_shape.set(1, max_y); + output_shape.set(2, depth); + + return output_shape; +} + +bool DepthConcatenateLayerNode::forward_descriptors() +{ + if(_outputs[0] != NullTensorID) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor DepthConcatenateLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + // Check if all input tensors are set + bool are_all_inputs_set = std::all_of(std::begin(_input_edges), std::end(_input_edges), [](const EdgeID & eid) + { + return eid != EmptyEdgeID; + }); + + TensorDescriptor output_info = {}; + + if(are_all_inputs_set) + { + std::vector inputs_shapes; + for(unsigned int i = 0; i < _input_edges.size(); ++i) + { + const Tensor *t = _graph->tensor(input_id(i)); + ARM_COMPUTE_ERROR_ON(t == nullptr); + inputs_shapes.push_back(t->desc().shape); + } + output_info = input(0)->desc(); + TensorShape output_shape = compute_output_shape(inputs_shapes); + output_info.shape = output_shape; + } + + return output_info; +} + +Status DepthConcatenateLayerNode::validate() +{ + ARM_COMPUTE_UNUSED(_total_nodes); + return Status{}; +} + +NodeType DepthConcatenateLayerNode::type() const +{ + return NodeType::DepthConcatenateLayer; +} + +void DepthConcatenateLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/DepthConvertLayer.cpp b/src/graph/nodes/DepthConvertLayer.cpp deleted file mode 100644 index 9b328e7b3e..0000000000 --- a/src/graph/nodes/DepthConvertLayer.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/DepthConvertLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" - -using namespace arm_compute::graph; - -DepthConvertLayer::DepthConvertLayer(const ConvertPolicy policy, uint32_t shift, DataType output_datatype) - : _policy(policy), _shift(shift), _output_datatype(output_datatype) -{ -} - -std::unique_ptr DepthConvertLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - _target_hint = ctx.hints().target_hint(); - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - - // Auto configure output - arm_compute::auto_init_if_empty(*out->info(), in->info()->tensor_shape(), 1, _output_datatype, in->info()->fixed_point_position()); - - // Create node context - NodeContext node_ctx(OperationType::DepthConvertLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - node_ctx.add_parameter("ConvertPolicy", _policy); - node_ctx.add_parameter("shift", _shift); - - // Get function - return OperationRegistry::get().find_operation(OperationType::DepthConvertLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/DepthwiseConvolutionLayer.cpp b/src/graph/nodes/DepthwiseConvolutionLayer.cpp deleted file mode 100644 index e5101cc33c..0000000000 --- a/src/graph/nodes/DepthwiseConvolutionLayer.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/DepthwiseConvolutionLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -std::unique_ptr DepthwiseConvolutionLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - if(_weights.tensor() == nullptr) - { - TensorShape weights_shape(_conv_width, _conv_height, input->tensor()->info()->tensor_shape().z()); - TensorInfo info = TensorInfo(TensorShape(weights_shape), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()); - info.set_quantization_info(_quant_info); - _weights.set_info(std::move(info)); - } - if(_biases.has_accessor() && _biases.tensor() == nullptr) - { - DataType dt = in->info()->data_type(); - _biases.set_info(TensorInfo(TensorShape(in->info()->dimension(2)), in->info()->num_channels(), is_data_type_quantized_asymmetric(dt) ? DataType::S32 : dt, in->info()->fixed_point_position())); - } - - bool weights_is_loaded = _weights.tensor() != nullptr; - bool biases_is_loaded = _biases.has_accessor() ? _biases.tensor() != nullptr : true; - - _weights.set_target(_target_hint); - if(_biases.has_accessor()) - { - _biases.set_target(_target_hint); - } - - // Create node context - NodeContext node_ctx(OperationType::DepthwiseConvolutionLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_input(_weights.tensor()); - if(_biases.has_accessor()) - { - node_ctx.add_input(_biases.tensor()); - } - node_ctx.add_output(out); - node_ctx.add_parameter("ConvolutionInfo", _conv_info); - node_ctx.add_parameter("Optimized3x3", _opt3x3); - - // Configure operation - auto func = OperationRegistry::get().find_operation(OperationType::DepthwiseConvolutionLayer, _target_hint)->configure(node_ctx); - - // Fill tensors - if(!weights_is_loaded) - { - _weights.allocate_and_fill_if_needed(); - } - if(!biases_is_loaded) - { - _biases.allocate_and_fill_if_needed(); - } - - // Get function - return func; -} diff --git a/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp b/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp new file mode 100644 index 0000000000..67a39029e6 --- /dev/null +++ b/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/DepthwiseConvolutionLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +DepthwiseConvolutionLayerNode::DepthwiseConvolutionLayerNode(PadStrideInfo info, DepthwiseConvolutionMethod method) + : _info(std::move(info)), _method(method) +{ + _input_edges.resize(3, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +void DepthwiseConvolutionLayerNode::set_depthwise_convolution_method(DepthwiseConvolutionMethod method) +{ + _method = method; +} + +DepthwiseConvolutionMethod DepthwiseConvolutionLayerNode::depthwise_convolution_method() const +{ + return _method; +} + +PadStrideInfo DepthwiseConvolutionLayerNode::convolution_info() const +{ + return _info; +} + +TensorShape DepthwiseConvolutionLayerNode::compute_output_shape(TensorShape input_shape, TensorShape weights_shape, PadStrideInfo info) +{ + unsigned int output_width = 0; + unsigned int output_height = 0; + std::tie(output_width, output_height) = scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), info); + + TensorShape output_shape{ input_shape }; + output_shape.set(0, output_width); + output_shape.set(1, output_height); + + return output_shape; +} + +bool DepthwiseConvolutionLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (input_id(1) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor DepthwiseConvolutionLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + const Tensor *src = input(0); + const Tensor *weights = input(1); + + ARM_COMPUTE_ERROR_ON(src == nullptr || weights == nullptr); + + TensorDescriptor output_info = src->desc(); + TensorShape output_shape = compute_output_shape(src->desc().shape, weights->desc().shape, _info); + output_info.shape = output_shape; + return output_info; +} + +Status DepthwiseConvolutionLayerNode::validate() +{ + return Status{}; +} + +NodeType DepthwiseConvolutionLayerNode::type() const +{ + return NodeType::DepthwiseConvolutionLayer; +} + +void DepthwiseConvolutionLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/EltwiseLayerNode.cpp b/src/graph/nodes/EltwiseLayerNode.cpp new file mode 100644 index 0000000000..b794043f2f --- /dev/null +++ b/src/graph/nodes/EltwiseLayerNode.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/EltwiseLayerNode.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +EltwiseLayerNode::EltwiseLayerNode(EltwiseOperation op) + : _op(op) +{ + _input_edges.resize(2, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +EltwiseOperation EltwiseLayerNode::eltwise_operation() const +{ + return _op; +} + +bool EltwiseLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor EltwiseLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_UNUSED(_op); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + return src->desc(); +} + +Status EltwiseLayerNode::validate() +{ + return Status{}; +} + +NodeType EltwiseLayerNode::type() const +{ + return NodeType::EltwiseLayer; +} + +void EltwiseLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/nodes/FlattenLayer.cpp b/src/graph/nodes/FlattenLayer.cpp deleted file mode 100644 index ea08296ba3..0000000000 --- a/src/graph/nodes/FlattenLayer.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/FlattenLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -std::unique_ptr FlattenLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - _target_hint = ctx.hints().target_hint(); - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - - // Auto configure output - TensorShape tensor_shape = in->info()->tensor_shape(); - tensor_shape.collapse(in->info()->num_dimensions()); - arm_compute::auto_init_if_empty(*out->info(), tensor_shape, 1, in->info()->data_type(), in->info()->fixed_point_position()); - - // Create node context - NodeContext node_ctx(OperationType::FlattenLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - - // Get function - return OperationRegistry::get().find_operation(OperationType::FlattenLayer, _target_hint)->configure(node_ctx); -} \ No newline at end of file diff --git a/src/graph/nodes/FlattenLayerNode.cpp b/src/graph/nodes/FlattenLayerNode.cpp new file mode 100644 index 0000000000..8b847c7056 --- /dev/null +++ b/src/graph/nodes/FlattenLayerNode.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/FlattenLayerNode.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +FlattenLayerNode::FlattenLayerNode() +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +bool FlattenLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor FlattenLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + TensorDescriptor output_desc = src->desc(); + output_desc.shape.collapse(src->desc().shape.num_dimensions()); + + return output_desc; +} + +Status FlattenLayerNode::validate() +{ + return Status{}; +} + +NodeType FlattenLayerNode::type() const +{ + return NodeType::FlattenLayer; +} + +void FlattenLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/FloorLayer.cpp b/src/graph/nodes/FloorLayer.cpp deleted file mode 100644 index 8750546ed9..0000000000 --- a/src/graph/nodes/FloorLayer.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/FloorLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -std::unique_ptr FloorLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::FloorLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - - // Get function - return OperationRegistry::get().find_operation(OperationType::FloorLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/FullyConnectedLayer.cpp b/src/graph/nodes/FullyConnectedLayer.cpp index 3742150d37..cbf2b35ddd 100644 --- a/src/graph/nodes/FullyConnectedLayer.cpp +++ b/src/graph/nodes/FullyConnectedLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -21,18 +21,40 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "arm_compute/graph/nodes/FullyConnectedLayer.h" +#include "arm_compute/graph/nodes/FullyConnectedLayerNode.h" -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" -using namespace arm_compute::graph; +namespace arm_compute +{ +namespace graph +{ +FullyConnectedLayerNode::FullyConnectedLayerNode(unsigned int num_outputs) + : _num_outputs(num_outputs) +{ + _input_edges.resize(3, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} -namespace +TensorShape FullyConnectedLayerNode::compute_weights_shape(TensorShape input_shape, unsigned int num_outputs) { -TensorShape calculate_fullyconnected_layer_output_shape(const TensorShape &input_shape, unsigned int output_neurons) + unsigned int num_weights = 1; + unsigned int num_dimensions = input_shape.num_dimensions(); + // Ignore the batch dimension if there is one: + if(num_dimensions == 2 || num_dimensions == 4) + { + num_dimensions--; + } + for(unsigned int i = 0; i < num_dimensions; i++) + { + num_weights *= input_shape[i]; + } + return TensorShape(num_weights, num_outputs); +} + +TensorShape FullyConnectedLayerNode::compute_output_shape(TensorShape input_shape, unsigned int num_outputs) { // Note: Only 1D batch space is supported at the moment unsigned int batches = input_shape[1]; @@ -40,67 +62,46 @@ TensorShape calculate_fullyconnected_layer_output_shape(const TensorShape &input { batches = input_shape[3]; } - return TensorShape(output_neurons, batches); + return TensorShape(num_outputs, batches); } -} // namespace -std::unique_ptr FullyConnectedLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) +bool FullyConnectedLayerNode::forward_descriptors() { - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - if(_weights.tensor() == nullptr) - { - unsigned int num_weights = 1; - unsigned int num_dimensions = in->info()->num_dimensions(); - // Ignore the batch dimension if there is one: - if(num_dimensions == 2 || num_dimensions == 4) - { - num_dimensions--; - } - for(unsigned int i = 0; i < num_dimensions; i++) - { - num_weights *= in->info()->dimension(i); - } - _weights.set_info(TensorInfo(TensorShape(num_weights, _num_neurons), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); - } - if(_biases.tensor() == nullptr) + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) { - _biases.set_info(TensorInfo(TensorShape(_num_neurons), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position())); + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; } + return false; +} - // Auto configure output - arm_compute::auto_init_if_empty(*out->info(), - calculate_fullyconnected_layer_output_shape(in->info()->tensor_shape(), _num_neurons), - in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()); - - bool weights_are_loaded = _weights.tensor() != nullptr; - bool biases_are_loaded = _biases.tensor() != nullptr; +TensorDescriptor FullyConnectedLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); - // Create node context - NodeContext node_ctx(OperationType::FullyConnectedLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_input(_weights.set_target(_target_hint)); - node_ctx.add_input(_biases.set_target(_target_hint)); - node_ctx.add_output(out); + TensorDescriptor output_info = src->desc(); + TensorShape output_shape = compute_output_shape(src->desc().shape, _num_outputs); + output_info.shape = output_shape; + return output_info; +} - // Configure operation - auto func = OperationRegistry::get().find_operation(OperationType::FullyConnectedLayer, _target_hint)->configure(node_ctx); +Status FullyConnectedLayerNode::validate() +{ + return Status{}; +} - // Fill biases - if(!weights_are_loaded) - { - _weights.allocate_and_fill_if_needed(); - } - if(!biases_are_loaded) - { - _biases.allocate_and_fill_if_needed(); - } +NodeType FullyConnectedLayerNode::type() const +{ + return NodeType::FullyConnectedLayer; +} - // Get function - return func; +void FullyConnectedLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); } +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/InputNode.cpp b/src/graph/nodes/InputNode.cpp new file mode 100644 index 0000000000..e912633a66 --- /dev/null +++ b/src/graph/nodes/InputNode.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/InputNode.h" + +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +InputNode::InputNode(TensorDescriptor desc) + : _desc(desc) +{ + _outputs.resize(1, NullTensorID); +} + +bool InputNode::forward_descriptors() +{ + if(output_id(0) != NullTensorID) + { + Tensor *t = output(0); + ARM_COMPUTE_ERROR_ON(t == nullptr); + t->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor InputNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + return _desc; +} + +Status InputNode::validate() +{ + return Status{}; +} + +NodeType InputNode::type() const +{ + return NodeType::Input; +} + +void InputNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/nodes/L2NormalizeLayer.cpp b/src/graph/nodes/L2NormalizeLayer.cpp deleted file mode 100644 index 9813ba4450..0000000000 --- a/src/graph/nodes/L2NormalizeLayer.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/L2NormalizeLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -L2NormalizeLayer::L2NormalizeLayer(unsigned int axis, float epsilon) - : _axis(axis), _epsilon(epsilon) -{ -} - -std::unique_ptr L2NormalizeLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::L2NormalizeLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - node_ctx.add_parameter("axis", _axis); - node_ctx.add_parameter("epsilon", _epsilon); - - // Get function - return OperationRegistry::get().find_operation(OperationType::L2NormalizeLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/NormalizationLayer.cpp b/src/graph/nodes/NormalizationLayer.cpp deleted file mode 100644 index a489329243..0000000000 --- a/src/graph/nodes/NormalizationLayer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/NormalizationLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -NormalizationLayer::NormalizationLayer(const NormalizationLayerInfo norm_info) - : _norm_info(norm_info) -{ -} - -std::unique_ptr NormalizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::NormalizationLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - node_ctx.add_parameter("NormalizationLayerInfo", _norm_info); - - // Get function - return OperationRegistry::get().find_operation(OperationType::NormalizationLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/NormalizationLayerNode.cpp b/src/graph/nodes/NormalizationLayerNode.cpp new file mode 100644 index 0000000000..a9f2fbd066 --- /dev/null +++ b/src/graph/nodes/NormalizationLayerNode.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/NormalizationLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +NormalizationLayerNode::NormalizationLayerNode(NormalizationLayerInfo norm_info) + : _info(norm_info) +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +NormalizationLayerInfo NormalizationLayerNode::normalization_info() const +{ + return _info; +} + +bool NormalizationLayerNode::forward_descriptors() +{ + if(input_id(0) != NullTensorID && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor NormalizationLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + return src->desc(); +} + +Status NormalizationLayerNode::validate() +{ + return Status{}; +} + +NodeType NormalizationLayerNode::type() const +{ + return NodeType::NormalizationLayer; +} + +void NormalizationLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/OutputNode.cpp b/src/graph/nodes/OutputNode.cpp new file mode 100644 index 0000000000..4c63bfa20c --- /dev/null +++ b/src/graph/nodes/OutputNode.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/OutputNode.h" + +#include "arm_compute/core/Error.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" +#include "arm_compute/graph/Tensor.h" + +namespace arm_compute +{ +namespace graph +{ +OutputNode::OutputNode() +{ + _input_edges.resize(1, EmptyEdgeID); +} + +bool OutputNode::forward_descriptors() +{ + return true; +} + +TensorDescriptor OutputNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + return TensorDescriptor(); +} + +Status OutputNode::validate() +{ + return Status{}; +} + +NodeType OutputNode::type() const +{ + return NodeType::Output; +} + +void OutputNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute diff --git a/src/graph/nodes/PoolingLayer.cpp b/src/graph/nodes/PoolingLayer.cpp deleted file mode 100644 index 2c151194f3..0000000000 --- a/src/graph/nodes/PoolingLayer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/PoolingLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -PoolingLayer::PoolingLayer(const PoolingLayerInfo pool_info) - : _pool_info(pool_info) -{ -} - -std::unique_ptr PoolingLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::PoolingLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - node_ctx.add_parameter("PoolingLayerInfo", _pool_info); - - // Get function - return OperationRegistry::get().find_operation(OperationType::PoolingLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/PoolingLayerNode.cpp b/src/graph/nodes/PoolingLayerNode.cpp new file mode 100644 index 0000000000..a7b6b3679a --- /dev/null +++ b/src/graph/nodes/PoolingLayerNode.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/PoolingLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +PoolingLayerNode::PoolingLayerNode(PoolingLayerInfo pool_info) + : _info(std::move(pool_info)) +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +PoolingLayerInfo PoolingLayerNode::pooling_info() const +{ + return _info; +} + +TensorShape PoolingLayerNode::compute_output_shape(TensorShape input_shape, PoolingLayerInfo info) +{ + const int pool_size_x = info.is_global_pooling() ? input_shape.x() : info.pool_size().width; + const int pool_size_y = info.is_global_pooling() ? input_shape.y() : info.pool_size().height; + + unsigned int pooled_width = 0; + unsigned int pooled_height = 0; + std::tie(pooled_width, pooled_height) = scaled_dimensions(input_shape.x(), input_shape.y(), pool_size_x, pool_size_y, info.pad_stride_info()); + + TensorShape output_shape{ input_shape }; + output_shape.set(0, pooled_width); + output_shape.set(1, pooled_height); + + return output_shape; +} + +bool PoolingLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor PoolingLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + TensorDescriptor output_info = src->desc(); + TensorShape output_shape = compute_output_shape(src->desc().shape, _info); + output_info.shape = output_shape; + return output_info; +} + +Status PoolingLayerNode::validate() +{ + return Status{}; +} + +NodeType PoolingLayerNode::type() const +{ + return NodeType::PoolingLayer; +} + +void PoolingLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/QuantizationLayer.cpp b/src/graph/nodes/QuantizationLayer.cpp deleted file mode 100644 index c102f47633..0000000000 --- a/src/graph/nodes/QuantizationLayer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/QuantizationLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" - -using namespace arm_compute::graph; - -std::unique_ptr QuantizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - _target_hint = ctx.hints().target_hint(); - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - - // Create node context - NodeContext node_ctx(OperationType::QuantizationLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - - // Get function - return OperationRegistry::get().find_operation(OperationType::QuantizationLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/ReshapeLayer.cpp b/src/graph/nodes/ReshapeLayer.cpp index b0c117e418..2757f06bd3 100644 --- a/src/graph/nodes/ReshapeLayer.cpp +++ b/src/graph/nodes/ReshapeLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -21,37 +21,61 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "arm_compute/graph/nodes/ReshapeLayer.h" +#include "arm_compute/graph/nodes/ReshapeLayerNode.h" -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" -using namespace arm_compute::graph; - -ReshapeLayer::ReshapeLayer(TensorShape shape) +namespace arm_compute +{ +namespace graph +{ +ReshapeLayerNode::ReshapeLayerNode(TensorShape shape) : _shape(shape) { + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); } -std::unique_ptr ReshapeLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) +bool ReshapeLayerNode::forward_descriptors() { - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor ReshapeLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); - _target_hint = ctx.hints().target_hint(); - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); + TensorDescriptor output_desc = src->desc(); + output_desc.shape = _shape; - // Auto configure output - arm_compute::auto_init_if_empty(*out->info(), _shape, 1, in->info()->data_type(), in->info()->fixed_point_position(), in->info()->quantization_info()); + return output_desc; +} - // Create node context - NodeContext node_ctx(OperationType::ReshapeLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); +Status ReshapeLayerNode::validate() +{ + return Status{}; +} - // Get function - return OperationRegistry::get().find_operation(OperationType::ReshapeLayer, _target_hint)->configure(node_ctx); +NodeType ReshapeLayerNode::type() const +{ + return NodeType::ReshapeLayer; +} + +void ReshapeLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); } +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/ResidualLayer.cpp b/src/graph/nodes/ResidualLayer.cpp deleted file mode 100644 index 87404f9e1f..0000000000 --- a/src/graph/nodes/ResidualLayer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/ResidualLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/Graph.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "arm_compute/graph/SubGraph.h" -#include "arm_compute/graph/Tensor.h" -#include "arm_compute/runtime/IFunction.h" -#include "support/ToolchainSupport.h" -#include "utils/Utils.h" - -#include -#include -#include - -using namespace arm_compute::graph; - -/** Residual function */ -class ResidualFunction final : public arm_compute::IFunction -{ -public: - /** Default Constructor */ - ResidualFunction(GraphContext &ctx, ITensorObject *output) - : _ctx(ctx), _input(nullptr), _output(output), _func(nullptr), _graphs(), _graph_outputs() - { - } - - /** Prevent instances from being copy constructed */ - ResidualFunction(const ResidualFunction &) = delete; - /** Prevent instances from being copy assigned */ - const ResidualFunction &operator=(const ResidualFunction &) = delete; - /** Prevent instances from being move constructed */ - ResidualFunction(ResidualFunction &&) = delete; - /** Prevent instances from being move assigned */ - ResidualFunction &operator=(ResidualFunction &&) = delete; - /** Default destructor */ - ~ResidualFunction() override = default; - - /** Set the input (when using only one sub graph) - * - * @param[in] input Input to set - */ - void set_input(std::unique_ptr input) - { - _input = std::move(input); - } - - /** Registers graph to be executed by the residual function - * - * @param[in] graph Graph to register - * @param[in] output Output to register - */ - void register_graph(std::unique_ptr graph, std::unique_ptr output) - { - _graphs.push_back(std::move(graph)); - _graph_outputs.push_back(std::move(output)); - } - - /** Configure the function */ - void configure() - { - ARM_COMPUTE_ERROR_ON(_graphs.size() < 1 || _graphs.size() > 2); - TargetHint target_hint = _ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::ArithmeticAddition); - node_ctx.set_target(target_hint); - - if(_graphs.size() == 1) - { - arm_compute::ITensor *in = _input->tensor(); - node_ctx.add_input(in); - } - - for(auto &o : _graph_outputs) - { - arm_compute::ITensor *in = o->tensor(); - node_ctx.add_input(in); - } - - arm_compute::ITensor *out = _output->tensor(); - auto_init_if_empty(*out->info(), *_graph_outputs[0]->tensor()->info()); - node_ctx.add_output(out); - - _func = OperationRegistry::get().find_operation(OperationType::ArithmeticAddition, target_hint)->configure(node_ctx); - - for(auto &o : _graph_outputs) - { - o->allocate(); - } - } - - // Inherited methods overriden: - void run() override - { - ARM_COMPUTE_ERROR_ON(_graphs.size() < 1 || _graphs.size() > 2); - - for(auto &g : _graphs) - { - ARM_COMPUTE_ERROR_ON(g.get() == nullptr); - g->run(); - } - - _func->run(); - } - -private: - GraphContext _ctx; - std::unique_ptr _input; - ITensorObject *_output; - std::unique_ptr _func; - std::vector> _graphs; - std::vector> _graph_outputs; -}; - -std::unique_ptr ResidualLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - ARM_COMPUTE_ERROR_ON(dynamic_cast(input) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(output) == nullptr); - - // Create residual function - auto func = arm_compute::support::cpp14::make_unique(ctx, output); - - if(_sub_graphs.size() == 1) - { - std::unique_ptr original_in; - original_in = arm_compute::support::cpp14::make_unique(*dynamic_cast(input), - input->tensor()->info()->tensor_shape(), - Coordinates()); - func->set_input(std::move(original_in)); - } - - // Constuct all sub-graphs given the input/output - for(auto &sg : _sub_graphs) - { - ARM_COMPUTE_ERROR_ON(sg.get() == nullptr); - - // IO buffers - std::unique_ptr in; - std::unique_ptr out; - std::unique_ptr func_in; - - // Create input sub-tensor - if(!sg->has_input()) - { - in = arm_compute::support::cpp14::make_unique(*dynamic_cast(input), - input->tensor()->info()->tensor_shape(), - Coordinates()); - } - - // Create output sub-tensor - if(!sg->has_output()) - { - ITensorInfo *info = input->tensor()->info(); - func_in = arm_compute::support::cpp14::make_unique(TensorInfo(info->num_channels(), info->data_type(), info->fixed_point_position())); - func_in->set_target(ctx.hints().target_hint()); - out = arm_compute::support::cpp14::make_unique(func_in->tensor(), - TensorShape(), - Coordinates(0, 0, 0), - func_in->target(), - true); - } - - // Construct sub_graph - auto g = sg->construct(ctx, std::move(in), std::move(out)); - - // Register graph to function - func->register_graph(std::move(g), std::move(func_in)); - } - - func->configure(); - - return std::move(func); -} diff --git a/src/graph/nodes/SoftmaxLayer.cpp b/src/graph/nodes/SoftmaxLayer.cpp deleted file mode 100644 index 7f2325b312..0000000000 --- a/src/graph/nodes/SoftmaxLayer.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2017 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/graph/nodes/SoftmaxLayer.h" - -#include "arm_compute/graph/Error.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistry.h" -#include "support/ToolchainSupport.h" - -using namespace arm_compute::graph; - -std::unique_ptr SoftmaxLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output) -{ - ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output); - - arm_compute::ITensor *in = input->tensor(); - arm_compute::ITensor *out = output->tensor(); - _target_hint = ctx.hints().target_hint(); - - // Create node context - NodeContext node_ctx(OperationType::SoftmaxLayer); - node_ctx.set_target(_target_hint); - node_ctx.add_input(in); - node_ctx.add_output(out); - - // Get function - return OperationRegistry::get().find_operation(OperationType::SoftmaxLayer, _target_hint)->configure(node_ctx); -} diff --git a/src/graph/nodes/SoftmaxLayerNode.cpp b/src/graph/nodes/SoftmaxLayerNode.cpp new file mode 100644 index 0000000000..4c21ac6ad0 --- /dev/null +++ b/src/graph/nodes/SoftmaxLayerNode.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/SoftmaxLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +SoftmaxLayerNode::SoftmaxLayerNode(float beta) + : _beta(beta) +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(1, NullTensorID); +} + +float SoftmaxLayerNode::beta() const +{ + return _beta; +} + +bool SoftmaxLayerNode::forward_descriptors() +{ + if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID)) + { + Tensor *dst = output(0); + ARM_COMPUTE_ERROR_ON(dst == nullptr); + dst->desc() = configure_output(0); + return true; + } + return false; +} + +TensorDescriptor SoftmaxLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + return src->desc(); +} + +Status SoftmaxLayerNode::validate() +{ + return Status{}; +} + +NodeType SoftmaxLayerNode::type() const +{ + return NodeType::SoftmaxLayer; +} + +void SoftmaxLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/nodes/SplitLayerNode.cpp b/src/graph/nodes/SplitLayerNode.cpp new file mode 100644 index 0000000000..c8fb43c2a1 --- /dev/null +++ b/src/graph/nodes/SplitLayerNode.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/nodes/SplitLayerNode.h" + +#include "arm_compute/core/Utils.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/INodeVisitor.h" + +namespace arm_compute +{ +namespace graph +{ +SplitLayerNode::SplitLayerNode(unsigned int num_splits, unsigned int axis) + : _num_splits(num_splits), _axis(axis) +{ + _input_edges.resize(1, EmptyEdgeID); + _outputs.resize(num_splits, NullTensorID); +} + +unsigned int SplitLayerNode::num_splits() const +{ + return _num_splits; +} + +unsigned int SplitLayerNode::axis() const +{ + return _axis; +} + +std::pair SplitLayerNode::compute_output_shape(TensorShape input_shape, unsigned int num_splits, unsigned int axis, unsigned int idx) +{ + ARM_COMPUTE_ERROR_ON(axis >= input_shape.num_dimensions()); + ARM_COMPUTE_ERROR_ON_MSG(input_shape[axis] % num_splits, "Split should be exact"); + + const unsigned int split_size = input_shape[axis] / num_splits; + + TensorShape output_shape = input_shape; + output_shape.set(axis, split_size); + + Coordinates coords; + coords.set(axis, idx * split_size); + + return std::make_pair(output_shape, coords); +} + +bool SplitLayerNode::forward_descriptors() +{ + if(input_id(0) != NullTensorID) + { + for(unsigned int i = 0; i < _outputs.size(); ++i) + { + if(output_id(i) != NullTensorID) + { + Tensor *dst_i = output(i); + ARM_COMPUTE_ERROR_ON(dst_i == nullptr); + dst_i->desc() = configure_output(i); + } + } + return true; + } + return false; +} + +TensorDescriptor SplitLayerNode::configure_output(size_t idx) const +{ + ARM_COMPUTE_UNUSED(idx); + ARM_COMPUTE_ERROR_ON(idx >= _outputs.size()); + + const Tensor *src = input(0); + ARM_COMPUTE_ERROR_ON(src == nullptr); + + TensorShape output_shape; + + TensorDescriptor output_info = src->desc(); + std::tie(output_shape, std::ignore) = compute_output_shape(src->desc().shape, _num_splits, _axis, idx); + output_info.shape = output_shape; + + return output_info; +} + +Status SplitLayerNode::validate() +{ + return Status{}; +} + +NodeType SplitLayerNode::type() const +{ + return NodeType::SplitLayer; +} + +void SplitLayerNode::accept(INodeVisitor &v) +{ + v.visit(*this); +} +} // namespace graph +} // namespace arm_compute \ No newline at end of file diff --git a/src/graph/operations/CLSimpleOperations.cpp b/src/graph/operations/CLSimpleOperations.cpp deleted file mode 100644 index fe56122009..0000000000 --- a/src/graph/operations/CLSimpleOperations.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/Error.h" -#include "arm_compute/graph/IOperation.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistrar.h" -#include "arm_compute/graph/Types.h" -#include "arm_compute/runtime/CL/CLFunctions.h" -#include "support/ToolchainSupport.h" -#include "utils/GraphTypePrinter.h" -#include "utils/TypePrinter.h" - -#include - -using namespace arm_compute::graph; - -/* Activation Layer */ -REGISTER_SIMPLE_OPERATION(CLActivationLayerOperation, OPENCL, OperationType::ActivationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto act_info = ctx.parameter("ActivationLayerInfo"); - - // Create and configure function - auto activation = arm_compute::support::cpp14::make_unique(); - activation->configure(in, out, act_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLActivationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Activation function: " << act_info.activation() - << " a: " << act_info.a() - << " b: " << act_info.b() - << std::endl); - - return std::move(activation); -} - -/* Arithmetic addition */ -REGISTER_SIMPLE_OPERATION(CLArithmeticAdditionOperation, OPENCL, OperationType::ArithmeticAddition) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in1 = dynamic_cast(ctx.input(0)); - auto *in2 = dynamic_cast(ctx.input(1)); - auto *out = dynamic_cast(ctx.output(0)); - - auto addition = arm_compute::support::cpp14::make_unique(); - addition->configure(in1, in2, out, ConvertPolicy::SATURATE); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLArithmeticAddition" - << " Data Type: " << in1->info()->data_type() - << " Input 1 shape: " << in1->info()->tensor_shape() - << " Input 2 shape: " << in2->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(addition); -} - -/* Batch Normalization Layer */ -REGISTER_SIMPLE_OPERATION(CLBatchNormalizationLayerOperation, OPENCL, OperationType::BatchNormalizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 5); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(2)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(3)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(4)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *mean = dynamic_cast(ctx.input(1)); - auto *var = dynamic_cast(ctx.input(2)); - auto *beta = dynamic_cast(ctx.input(3)); - auto *gamma = dynamic_cast(ctx.input(4)); - auto *out = dynamic_cast(ctx.output(0)); - const auto epsilon = ctx.parameter("epsilon"); - const auto act_info = ctx.parameter("act_info"); - - // Create and configure function - auto batch_norm = arm_compute::support::cpp14::make_unique(); - batch_norm->configure(in, out, mean, var, beta, gamma, epsilon, act_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLBatchNormalizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Mean shape: " << mean->info()->tensor_shape() - << " Var shape: " << var->info()->tensor_shape() - << " Beta shape: " << beta->info()->tensor_shape() - << " Gamma shape: " << gamma->info()->tensor_shape() - << " Epsilon: " << epsilon - << " Activation function: " << act_info.activation() - << " a: " << act_info.a() - << " b: " << act_info.b() - << std::endl); - - return std::move(batch_norm); -} - -/* DepthConvertLayer Layer */ -REGISTER_SIMPLE_OPERATION(CLDepthConvertLayerOperation, OPENCL, OperationType::DepthConvertLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto conv_policy = ctx.parameter("ConvertPolicy"); - const auto shift = ctx.parameter("shift"); - - // Create and configure function - auto depthconvert = arm_compute::support::cpp14::make_unique(); - depthconvert->configure(in, out, conv_policy, shift); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDepthConvertLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " shift: " << shift - << std::endl); - - return std::move(depthconvert); -} - -/* DepthwiseConvolutionLayer Layer */ -REGISTER_SIMPLE_OPERATION(CLDepthwiseConvolutionOperation, OPENCL, OperationType::DepthwiseConvolutionLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2 && ctx.num_inputs() != 3); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *weights = dynamic_cast(ctx.input(1)); - auto *biases = ctx.num_inputs() == 3 ? dynamic_cast(ctx.input(2)) : nullptr; - auto *out = dynamic_cast(ctx.output(0)); - const auto conv_info = ctx.parameter("ConvolutionInfo"); - const auto opt3x3 = ctx.parameter("Optimized3x3"); - - // Create and configure function - std::unique_ptr func; - bool run_3x3_opt = opt3x3 && weights->info()->dimension(0) == 3; - if(run_3x3_opt) - { - auto depwthwise_conv = arm_compute::support::cpp14::make_unique(); - depwthwise_conv->configure(in, weights, biases, out, conv_info); - func = std::move(depwthwise_conv); - } - else - { - auto depwthwise_conv = arm_compute::support::cpp14::make_unique(); - depwthwise_conv->configure(in, weights, biases, out, conv_info); - func = std::move(depwthwise_conv); - } - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDepthwiseConvolutionLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Weights shape: " << weights->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape()); - if(biases == nullptr) - { - ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: No biases provided" << std::endl); - } - else - { - ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: " << biases->info()->tensor_shape() << std::endl); - } - - return func; -} - -/* DeQuantizationLayer Layer */ -REGISTER_SIMPLE_OPERATION(CLDequantizationLayerOperation, OPENCL, OperationType::DequantizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 2); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(1)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - auto *min_max = dynamic_cast(ctx.output(1)); - - // Create and configure function - auto dequantization = arm_compute::support::cpp14::make_unique(); - dequantization->configure(in, out, min_max); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDequantizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Min max shape: " << min_max->info()->tensor_shape() - << std::endl); - - return std::move(dequantization); -} - -/* Flatten Layer */ -REGISTER_SIMPLE_OPERATION(CLFlattenLayerOperation, OPENCL, OperationType::FlattenLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto flatten = arm_compute::support::cpp14::make_unique(); - flatten->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFlattenLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(flatten); -} - -/* Floor Layer */ -REGISTER_SIMPLE_OPERATION(CLFloorLayerOperation, OPENCL, OperationType::FloorLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto floor = arm_compute::support::cpp14::make_unique(); - floor->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFloorLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(floor); -} - -/* Fully Connected Layer */ -REGISTER_SIMPLE_OPERATION(CLFullyConnectedLayer, OPENCL, OperationType::FullyConnectedLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 3); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(2)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *weights = dynamic_cast(ctx.input(1)); - auto *biases = dynamic_cast(ctx.input(2)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto fc = arm_compute::support::cpp14::make_unique(); - fc->configure(in, weights, biases, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFullyConnectedLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Weights shape: " << weights->info()->tensor_shape() - << " Biases Shape: " << biases->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(fc); -} - -/* L2 Normalize Layer */ -REGISTER_SIMPLE_OPERATION(CLL2NormalizeLayerOperation, OPENCL, OperationType::L2NormalizeLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto axis = ctx.parameter("axis"); - const auto epsilon = ctx.parameter("epsilon"); - - // Create and configure function - auto l2_norm = arm_compute::support::cpp14::make_unique(); - l2_norm->configure(in, out, axis, epsilon); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLL2NormalizeLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Axis: " << axis - << " Epsilon: " << epsilon - << std::endl); - - return std::move(l2_norm); -} - -/* Normalization Layer */ -REGISTER_SIMPLE_OPERATION(CLNormalizationLayerOperation, OPENCL, OperationType::NormalizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto norm_info = ctx.parameter("NormalizationLayerInfo"); - - // Create and configure function - auto norm = arm_compute::support::cpp14::make_unique(); - norm->configure(in, out, norm_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLNormalizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Normalization info: " << norm_info - << std::endl); - - return std::move(norm); -} - -/* Pooling Layer */ -REGISTER_SIMPLE_OPERATION(CLPoolingLayerOperation, OPENCL, OperationType::PoolingLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto pool_info = ctx.parameter("PoolingLayerInfo"); - - // Create and configure function - auto pool = arm_compute::support::cpp14::make_unique(); - pool->configure(in, out, pool_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLPoolingLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Pooling info: " << pool_info - << std::endl); - - return std::move(pool); -} - -/* Quantization Layer */ -REGISTER_SIMPLE_OPERATION(CLQuantizationLayerOperation, OPENCL, OperationType::QuantizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto quantization = arm_compute::support::cpp14::make_unique(); - quantization->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLQuantizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(quantization); -} - -/* Reshape Layer */ -REGISTER_SIMPLE_OPERATION(CLReshapeLayerOperation, OPENCL, OperationType::ReshapeLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto reshape = arm_compute::support::cpp14::make_unique(); - reshape->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLReshapeLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(reshape); -} - -/* Softmax Layer */ -REGISTER_SIMPLE_OPERATION(CLSoftmaxLayerOperation, OPENCL, OperationType::SoftmaxLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto smx = arm_compute::support::cpp14::make_unique(); - smx->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLSoftmaxLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(smx); -} diff --git a/src/graph/operations/NESimpleOperations.cpp b/src/graph/operations/NESimpleOperations.cpp deleted file mode 100644 index 4154b9a59c..0000000000 --- a/src/graph/operations/NESimpleOperations.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2017-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/core/Error.h" -#include "arm_compute/core/ITensor.h" -#include "arm_compute/graph/IOperation.h" -#include "arm_compute/graph/NodeContext.h" -#include "arm_compute/graph/OperationRegistrar.h" -#include "arm_compute/graph/Types.h" -#include "arm_compute/runtime/NEON/NEFunctions.h" -#include "support/ToolchainSupport.h" -#include "utils/GraphTypePrinter.h" -#include "utils/TypePrinter.h" - -#include - -using namespace arm_compute::graph; - -/* Activation Layer */ -REGISTER_SIMPLE_OPERATION(NEActivationLayerOperation, NEON, OperationType::ActivationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto act_info = ctx.parameter("ActivationLayerInfo"); - - // Create and configure function - auto activation = arm_compute::support::cpp14::make_unique(); - activation->configure(in, out, act_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEActivationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Activation function: " << act_info.activation() - << " a: " << act_info.a() - << " b: " << act_info.b() - << std::endl); - - return std::move(activation); -} - -/* Arithmetic addition */ -REGISTER_SIMPLE_OPERATION(NEArithmeticAdditionOperation, NEON, OperationType::ArithmeticAddition) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in1 = dynamic_cast(ctx.input(0)); - auto *in2 = dynamic_cast(ctx.input(1)); - auto *out = dynamic_cast(ctx.output(0)); - - auto addition = arm_compute::support::cpp14::make_unique(); - addition->configure(in1, in2, out, ConvertPolicy::SATURATE); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEArithmeticAddition" - << " Data Type: " << in1->info()->data_type() - << " Input 1 shape: " << in1->info()->tensor_shape() - << " Input 2 shape: " << in2->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(addition); -} - -/* Batch Normalization Layer */ -REGISTER_SIMPLE_OPERATION(NEBatchNormalizationLayerOperation, NEON, OperationType::BatchNormalizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 5); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(2)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(3)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(4)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *mean = dynamic_cast(ctx.input(1)); - auto *var = dynamic_cast(ctx.input(2)); - auto *beta = dynamic_cast(ctx.input(3)); - auto *gamma = dynamic_cast(ctx.input(4)); - auto *out = dynamic_cast(ctx.output(0)); - const auto epsilon = ctx.parameter("epsilon"); - const auto act_info = ctx.parameter("act_info"); - - // Create and configure function - auto batch_norm = arm_compute::support::cpp14::make_unique(); - batch_norm->configure(in, out, mean, var, beta, gamma, epsilon, act_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEBatchNormalizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Mean shape: " << mean->info()->tensor_shape() - << " Var shape: " << var->info()->tensor_shape() - << " Beta shape: " << beta->info()->tensor_shape() - << " Gamma shape: " << gamma->info()->tensor_shape() - << " Epsilon: " << epsilon - << " Activation function: " << act_info.activation() - << " a: " << act_info.a() - << " b: " << act_info.b() - << std::endl); - - return std::move(batch_norm); -} - -/* DepthConvertLayer Layer */ -REGISTER_SIMPLE_OPERATION(NEDepthConvertLayerOperation, NEON, OperationType::DepthConvertLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto conv_policy = ctx.parameter("ConvertPolicy"); - const auto shift = ctx.parameter("shift"); - - // Create and configure function - auto depthconvert = arm_compute::support::cpp14::make_unique(); - depthconvert->configure(in, out, conv_policy, shift); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDepthConvertLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " shift: " << shift - << std::endl); - - return std::move(depthconvert); -} - -/* DepthwiseConvolutionLayer Layer */ -REGISTER_SIMPLE_OPERATION(NEDepthwiseConvolutionOperation, NEON, OperationType::DepthwiseConvolutionLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2 && ctx.num_inputs() != 3); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *weights = dynamic_cast(ctx.input(1)); - auto *biases = ctx.num_inputs() == 3 ? dynamic_cast(ctx.input(2)) : nullptr; - auto *out = dynamic_cast(ctx.output(0)); - const auto conv_info = ctx.parameter("ConvolutionInfo"); - const auto opt3x3 = ctx.parameter("Optimized3x3"); - - // Create and configure function - std::unique_ptr func; - bool run_3x3_opt = opt3x3 && weights->info()->dimension(0) == 3; - if(run_3x3_opt) - { - auto depwthwise_conv = arm_compute::support::cpp14::make_unique(); - depwthwise_conv->configure(in, weights, biases, out, conv_info); - func = std::move(depwthwise_conv); - } - else - { - auto depwthwise_conv = arm_compute::support::cpp14::make_unique(); - depwthwise_conv->configure(in, weights, biases, out, conv_info); - func = std::move(depwthwise_conv); - } - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDepthwiseConvolutionLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Weights shape: " << weights->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape()); - if(biases == nullptr) - { - ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: No biases provided" << std::endl); - } - else - { - ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: " << biases->info()->tensor_shape() << std::endl); - } - - return func; -} - -/* DeQuantizationLayer Layer */ -REGISTER_SIMPLE_OPERATION(NEDequantizationLayerOperation, NEON, OperationType::DequantizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 2); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(1)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - auto *min_max = dynamic_cast(ctx.output(1)); - - // Create and configure function - auto dequantization = arm_compute::support::cpp14::make_unique(); - dequantization->configure(in, out, min_max); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDequantizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Min max shape: " << min_max->info()->tensor_shape() - << std::endl); - - return std::move(dequantization); -} - -/* Flatten Layer */ -REGISTER_SIMPLE_OPERATION(NEFlattenLayerOperation, NEON, OperationType::FlattenLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto flatten = arm_compute::support::cpp14::make_unique(); - flatten->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFlattenLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(flatten); -} - -/* Floor Layer */ -REGISTER_SIMPLE_OPERATION(NEFloorLayerOperation, NEON, OperationType::FloorLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto floor = arm_compute::support::cpp14::make_unique(); - floor->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFloorLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(floor); -} - -/* Fully Connected Layer */ -REGISTER_SIMPLE_OPERATION(NEFullyConnectedLayer, NEON, OperationType::FullyConnectedLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 3); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(1)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(2)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *weights = dynamic_cast(ctx.input(1)); - auto *biases = dynamic_cast(ctx.input(2)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto fc = arm_compute::support::cpp14::make_unique(); - fc->configure(in, weights, biases, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFullyConnectedLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Weights shape: " << weights->info()->tensor_shape() - << " Biases Shape: " << biases->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(fc); -} - -/* L2 Normalize Layer */ -REGISTER_SIMPLE_OPERATION(NEL2NormalizeLayerOperation, NEON, OperationType::L2NormalizeLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto axis = ctx.parameter("axis"); - const auto epsilon = ctx.parameter("epsilon"); - - // Create and configure function - auto l2_norm = arm_compute::support::cpp14::make_unique(); - l2_norm->configure(in, out, axis, epsilon); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEL2NormalizeLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Axis: " << axis - << " Epsilon: " << epsilon - << std::endl); - - return std::move(l2_norm); -} - -/* Normalization Layer */ -REGISTER_SIMPLE_OPERATION(NENormalizationLayerOperation, NEON, OperationType::NormalizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto norm_info = ctx.parameter("NormalizationLayerInfo"); - - // Create and configure function - auto norm = arm_compute::support::cpp14::make_unique(); - norm->configure(in, out, norm_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NENormalizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Normalization info: " << norm_info - << std::endl); - - return std::move(norm); -} - -/* Pooling Layer */ -REGISTER_SIMPLE_OPERATION(NEPoolingLayerOperation, NEON, OperationType::PoolingLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - const auto pool_info = ctx.parameter("PoolingLayerInfo"); - - // Create and configure function - auto pool = arm_compute::support::cpp14::make_unique(); - pool->configure(in, out, pool_info); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEPoolingLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << " Pooling info: " << pool_info - << std::endl); - - return std::move(pool); -} - -/* Quantization Layer */ -REGISTER_SIMPLE_OPERATION(NEQuantizationLayerOperation, NEON, OperationType::QuantizationLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto quantization = arm_compute::support::cpp14::make_unique(); - quantization->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEQuantizationLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(quantization); -} - -/* Reshape Layer */ -REGISTER_SIMPLE_OPERATION(NEReshapeLayerOperation, NEON, OperationType::ReshapeLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto reshape = arm_compute::support::cpp14::make_unique(); - reshape->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEReshapeLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(reshape); -} - -/* Softmax Layer */ -REGISTER_SIMPLE_OPERATION(NESoftmaxLayerOperation, NEON, OperationType::SoftmaxLayer) -{ - ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1); - ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.input(0)) == nullptr); - ARM_COMPUTE_ERROR_ON(dynamic_cast(ctx.output(0)) == nullptr); - - // Extract IO and info - auto *in = dynamic_cast(ctx.input(0)); - auto *out = dynamic_cast(ctx.output(0)); - - // Create and configure function - auto smx = arm_compute::support::cpp14::make_unique(); - smx->configure(in, out); - - // Log info - ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NESoftmaxLayer" - << " Data Type: " << in->info()->data_type() - << " Input shape: " << in->info()->tensor_shape() - << " Output shape: " << out->info()->tensor_shape() - << std::endl); - - return std::move(smx); -} diff --git a/src/graph/printers/DotGraphPrinter.cpp b/src/graph/printers/DotGraphPrinter.cpp new file mode 100644 index 0000000000..47b1bb56bf --- /dev/null +++ b/src/graph/printers/DotGraphPrinter.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/printers/DotGraphPrinter.h" + +#include "arm_compute/core/Error.h" +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Tensor.h" +#include "arm_compute/graph/TypePrinter.h" +#include "arm_compute/graph/nodes/Nodes.h" + +namespace arm_compute +{ +namespace graph +{ +void DotGraphVisitor::visit(ActivationLayerNode &n) +{ + std::stringstream ss; + ss << n.activation_info().activation(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(BatchNormalizationLayerNode &n) +{ + std::stringstream ss; + ss << (n.fused_activation().enabled() ? to_string(n.fused_activation().activation()) : ""); + _info = ss.str(); +} + +void DotGraphVisitor::visit(ConvolutionLayerNode &n) +{ + std::stringstream ss; + ss << n.convolution_method(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(DepthConcatenateLayerNode &n) +{ + std::stringstream ss; + ss << "Enabled: " << n.is_enabled(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(DepthwiseConvolutionLayerNode &n) +{ + std::stringstream ss; + ss << n.depthwise_convolution_method(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(EltwiseLayerNode &n) +{ + std::stringstream ss; + ss << n.eltwise_operation(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(NormalizationLayerNode &n) +{ + std::stringstream ss; + ss << n.normalization_info().type(); + _info = ss.str(); +} + +void DotGraphVisitor::visit(PoolingLayerNode &n) +{ + std::stringstream ss; + ss << n.pooling_info().pool_type(); + ss << R"( \n )"; + ss << n.pooling_info().pool_size(); + ss << R"( \n )"; + ss << n.pooling_info().pad_stride_info(); + _info = ss.str(); +} + +void DotGraphVisitor::default_visit() +{ + _info.clear(); +} + +const std::string &DotGraphVisitor::info() const +{ + return _info; +} + +void DotGraphPrinter::print(const Graph &g, std::ostream &os) +{ + // Print header + print_header(g, os); + + // Print nodes + print_nodes(g, os); + + // Print edges + print_edges(g, os); + + // Print footer + print_footer(g, os); +} + +void DotGraphPrinter::print_header(const Graph &g, std::ostream &os) +{ + // Print graph name + std::string graph_name = (g.name().empty()) ? "Graph" : g.name(); + os << "digraph " << graph_name << "{\n"; +} + +void DotGraphPrinter::print_footer(const Graph &g, std::ostream &os) +{ + ARM_COMPUTE_UNUSED(g); + os << "}\n"; +} + +void DotGraphPrinter::print_nodes(const Graph &g, std::ostream &os) +{ + for(const auto &n : g.nodes()) + { + if(n) + { + // Output node id + std::string node_id = std::string("n") + support::cpp11::to_string(n->id()); + os << node_id << " "; + + // Output label + n->accept(_dot_node_visitor); + + std::string name = n->name().empty() ? node_id : n->name(); + auto node_description = _dot_node_visitor.info(); + + os << R"([label = ")" << name << R"( \n )" << n->assigned_target() << R"( \n )" << node_description << R"("])"; + os << ";\n"; + } + } +} + +void DotGraphPrinter::print_edges(const Graph &g, std::ostream &os) +{ + for(const auto &e : g.edges()) + { + if(e) + { + std::string source_node_id = std::string("n") + support::cpp11::to_string(e->producer_id()); + std::string sink_node_id = std::string("n") + support::cpp11::to_string(e->consumer_id()); + os << source_node_id << " -> " << sink_node_id << " "; + const Tensor *t = e->tensor(); + ARM_COMPUTE_ERROR_ON(t == nullptr); + os << R"([label = ")" << t->desc().shape << R"( \n )" << t->desc().data_type << R"("])"; + os << ";\n"; + } + } +} +} // namespace graph +} // namespace arm_compute -- cgit v1.2.1