From 18655b8d326f6109c6fedacf42b46dc4bc942324 Mon Sep 17 00:00:00 2001 From: Cathal Corbett Date: Mon, 13 Dec 2021 13:03:22 +0000 Subject: IVGCVSW-6630 Add new method AddPrecompiledLayer() to INetwork * Add new method AddPrecompiledLayer() to INetwork with Comments noting it is for backend users. * Added unit test to SubgraphViewTests.cpp. * Bug fix and code refactor in Graph.cpp specifically around Graph::SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substituteLayer) Change-Id: If2d816e5109e48ce920bf92d8823b39130c23a16 Signed-off-by: Cathal Corbett --- include/armnn/INetwork.hpp | 13 ++++++ src/armnn/Graph.cpp | 17 +++----- src/armnn/Graph.hpp | 1 - src/armnn/Network.cpp | 31 +++++++++++++ src/armnn/Network.hpp | 4 ++ src/armnn/test/SubgraphViewTests.cpp | 85 ++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 13 deletions(-) diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp index 1d98821160..4da01a62db 100644 --- a/include/armnn/INetwork.hpp +++ b/include/armnn/INetwork.hpp @@ -225,6 +225,9 @@ class NetworkImpl; using INetworkPtr = std::unique_ptr; using IOptimizedNetworkPtr = std::unique_ptr; +using CompiledBlobDeleter = std::function; +using CompiledBlobPtr = std::unique_ptr; + /// Main network class which provides the interface for building up a neural network. /// This object is subsequently required by the IRuntime::Load() method. class INetwork @@ -413,6 +416,16 @@ public: IConnectableLayer* AddPooling3dLayer(const Pooling3dDescriptor& pooling3dDescriptor, const char* name = nullptr); + /// Adds a Precompiled layer to the network. + /// Method use is for backend users. + /// @param preCompiledDescriptor - PreCompiledDescriptor contains parameters for the Precompiled layer. + /// @param compiledBlobPtr - CompiledBlobPtr pre-compiled object set for the Precompiled layer. + /// @param backend - optional BackendId set for the Precompiled layer. + /// @return - Interface for configuring the layer. + IConnectableLayer* AddPrecompiledLayer(const PreCompiledDescriptor& preCompiledDescriptor, + CompiledBlobPtr& compiledBlobPtr, + const Optional& backend); + /// Adds an activation layer to the network. /// @param activationDescriptor - ActivationDescriptor to configure the activation. /// @param name - Optional name for the layer. diff --git a/src/armnn/Graph.cpp b/src/armnn/Graph.cpp index 0591bea99a..6d24e50bdc 100644 --- a/src/armnn/Graph.cpp +++ b/src/armnn/Graph.cpp @@ -435,8 +435,11 @@ void Graph::SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substi { ARMNN_ASSERT(substituteLayer != nullptr); - ReplaceSubgraphConnections(subgraph, substituteLayer); - EraseSubgraphLayers(subgraph); + // Create a new sub-graph with only the given layer, using + // the given sub-graph as a reference of which parent graph to use + SubgraphView substituteSubgraph(substituteLayer); + + SubstituteSubgraph(subgraph, substituteSubgraph); } void Graph::SubstituteSubgraph(SubgraphView& subgraph, const SubgraphView& substituteSubgraph) @@ -456,16 +459,6 @@ void Graph::SubstituteSubgraph(SubgraphView& subgraph, const SubgraphView& subst TopologicalSort(); } -void Graph::ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer) -{ - ARMNN_ASSERT(substituteLayer != nullptr); - - // Create a new sub-graph with only the given layer, using - // the given sub-graph as a reference of which parent graph to use - SubgraphView substituteSubgraph(substituteLayer); - ReplaceSubgraphConnections(subgraph, substituteSubgraph); -} - void Graph::ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph) { ARMNN_ASSERT_MSG(!substituteSubgraph.GetLayers().empty(), "New sub-graph used for substitution must not be empty"); diff --git a/src/armnn/Graph.hpp b/src/armnn/Graph.hpp index 74aefb23ee..d49b5e513f 100644 --- a/src/armnn/Graph.hpp +++ b/src/armnn/Graph.hpp @@ -263,7 +263,6 @@ private: std::unordered_set m_OutputIds; std::unordered_map m_PosInGraphMap; - void ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer); void ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph); void EraseSubgraphLayers(SubgraphView &subgraph); diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index 54d9ae2028..c6f3f914b5 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -214,6 +214,13 @@ IConnectableLayer* INetwork::AddPooling3dLayer(const Pooling3dDescriptor& poolin return pNetworkImpl->AddPooling3dLayer(pooling3dDescriptor, name); } +IConnectableLayer* INetwork::AddPrecompiledLayer(const PreCompiledDescriptor& preCompiledDescriptor, + CompiledBlobPtr& compiledBlobPtr, + const Optional& backend) +{ + return pNetworkImpl->AddPrecompiledLayer(preCompiledDescriptor, compiledBlobPtr, backend); +} + IConnectableLayer* INetwork::AddActivationLayer(const ActivationDescriptor& activationDescriptor, const char* name) { @@ -2763,6 +2770,30 @@ IConnectableLayer* NetworkImpl::AddUnidirectionalSequenceLstmLayer( return layer; } +IConnectableLayer* NetworkImpl::AddPrecompiledLayer(const PreCompiledDescriptor& preCompiledDescriptor, + CompiledBlobPtr& compiledBlobPtr, + const Optional& backend) +{ + // Method use is for backend users. + const auto layer = m_Graph->AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Assign the pre-compiled object to layer + // Pass only one compiled network, Arm NN does not handle multiple + // pre-compiled objects in a single pre-compiled layer currently + layer->SetPreCompiledObject(std::move(compiledBlobPtr)); + + if (backend.has_value()) + { + layer->SetBackendId(backend.value()); + } + else + { + layer->SetBackendId(layer->GetBackendHint().value()); + } + + return layer; +} + ARMNN_NO_DEPRECATE_WARN_BEGIN void NetworkImpl::Accept(ILayerVisitor& visitor) const { diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index 959d88dbed..3fdc140099 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -170,6 +170,10 @@ public: IConnectableLayer* AddPooling3dLayer(const Pooling3dDescriptor& pooling3dDescriptor, const char* name = nullptr); + IConnectableLayer* AddPrecompiledLayer(const PreCompiledDescriptor& preCompiledDescriptor, + CompiledBlobPtr& compiledBlobPtr, + const Optional& backend); + IConnectableLayer* AddPreluLayer(const char* name = nullptr); IConnectableLayer* AddQuantizeLayer(const char* name = nullptr); diff --git a/src/armnn/test/SubgraphViewTests.cpp b/src/armnn/test/SubgraphViewTests.cpp index d270787968..4e509be78b 100644 --- a/src/armnn/test/SubgraphViewTests.cpp +++ b/src/armnn/test/SubgraphViewTests.cpp @@ -168,6 +168,91 @@ TEST_CASE("SingleInputSingleOutput") CHECK_EQ(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn); } +TEST_CASE("SingleInputSingleOutputAddPrecompiledLayerSubstituteSubgraph1") +{ + // Construct graph. + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}), + CreateOutputsFrom({convLayer2}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn = subgraph->GetInputSlot(0)->GetConnection(); + IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0); + + PreCompiledDescriptor preCompiledDescriptor(1, 1); + CompiledBlobPtr compiledBlobPtr; + BackendId backend = Compute::CpuRef; + + // Construct dummy pre-compiled layer + INetworkPtr network = INetwork::Create(); + IConnectableLayer* preCompiledLayer = network->AddPrecompiledLayer(preCompiledDescriptor, compiledBlobPtr, backend); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(*subgraph, preCompiledLayer); + + // Check that connections are correct after substitution + CHECK_EQ(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn); + CHECK_EQ(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn); +} + +TEST_CASE("SingleInputSingleOutputAddPrecompiledLayerSubstituteSubgraph2") +{ + // Construct graph. + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(CreateInputsFrom({convLayer1}), + CreateOutputsFrom({convLayer2}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn = subgraph->GetInputSlot(0)->GetConnection(); + IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0); + + PreCompiledDescriptor preCompiledDescriptor(1, 1); + CompiledBlobPtr compiledBlobPtr; + BackendId backend = Compute::CpuRef; + + // Construct dummy pre-compiled layer + INetworkPtr network = INetwork::Create(); + IConnectableLayer* preCompiledLayer = network->AddPrecompiledLayer(preCompiledDescriptor, compiledBlobPtr, backend); + SubgraphView substituteSubgraph(preCompiledLayer); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(*subgraph, substituteSubgraph); + + // Check that connections are correct after substitution + CHECK_EQ(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn); + CHECK_EQ(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn); +} + TEST_CASE("SingleInputSingleOutputSubstituteGraph") { // Construct graph -- cgit v1.2.1