diff options
author | Jim Flynn <jim.flynn@arm.com> | 2020-03-19 17:03:14 +0000 |
---|---|---|
committer | Jim Flynn <jim.flynn@arm.com> | 2020-03-19 17:03:14 +0000 |
commit | 0e2bab81442ee6cc2b94e4f7881ed0c5c6af65e7 (patch) | |
tree | b0af08b5a0b74149fca422151127ac6310385399 /src/armnn/Graph.cpp | |
parent | 8c3259fa007d43fcc5ea56fe6928526dbe79f3c0 (diff) | |
download | armnn-0e2bab81442ee6cc2b94e4f7881ed0c5c6af65e7.tar.gz |
Creating gh-pages documentation for ArmNN
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
Diffstat (limited to 'src/armnn/Graph.cpp')
-rw-r--r-- | src/armnn/Graph.cpp | 520 |
1 files changed, 0 insertions, 520 deletions
diff --git a/src/armnn/Graph.cpp b/src/armnn/Graph.cpp deleted file mode 100644 index 8e7f75b2b8..0000000000 --- a/src/armnn/Graph.cpp +++ /dev/null @@ -1,520 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "Graph.hpp" -#include "SubgraphView.hpp" -#include "LayersFwd.hpp" - -#include <armnn/backends/IBackendInternal.hpp> - -#include <armnn/BackendId.hpp> -#include <armnn/Logging.hpp> -#include <armnn/TypesUtils.hpp> -#include <armnn/Utils.hpp> - -#include <boost/polymorphic_cast.hpp> -#include <boost/assert.hpp> -#include <boost/format.hpp> - -#include <unordered_map> -#include <DotSerializer.hpp> -#include <sstream> - -namespace armnn -{ - -Graph::Graph(const Graph& other) -: m_LayersInOrder(other.m_LayersInOrder) -{ - std::unordered_map<const Layer*, Layer*> otherToClonedMap; - - for (auto&& otherLayer : other.m_Layers) - { - Layer* const layer = otherLayer->Clone(*this); - otherToClonedMap.emplace(otherLayer, layer); - } - - // Copies slot connections. - for (auto&& otherLayer : other.m_Layers) - { - Layer* const thisLayer = otherToClonedMap[otherLayer]; - - auto outputSlot = thisLayer->BeginOutputSlots(); - for (auto&& otherOutputSlot : otherLayer->GetOutputSlots()) - { - for (auto&& otherInputSlot : otherOutputSlot.GetConnections()) - { - const Layer& otherTgtLayer = otherInputSlot->GetOwningLayer(); - Layer* const thisTgtLayer = otherToClonedMap[&otherTgtLayer]; - - InputSlot& inputSlot = thisTgtLayer->GetInputSlot(otherInputSlot->GetSlotIndex()); - outputSlot->Connect(inputSlot); - } - outputSlot->SetTensorInfo(otherOutputSlot.GetTensorInfo()); - ++outputSlot; - } - } -} - -Status Graph::Print() const -{ - if (m_Layers.empty()) - { - ARMNN_LOG(info) << "\n Graph is empty.\n"; - return Status::Success; - } - ARMNN_LOG(info) << "\n"; - ARMNN_LOG(info) << "Walking Pattern: \n"; - - for (auto&& it : TopologicalSort()) - { - ARMNN_LOG(info) << it->GetName() << ":" << GetLayerTypeAsCString(it->GetType()) - << ":" << it->GetBackendId().Get(); - } - ARMNN_LOG(info) << "\n\n"; - - return Status::Success; -} - -Status Graph::SerializeToDot(std::ostream& stream) -{ - { - DotGraph graph(stream, "Optimized"); - - { - // Default node attributes: - DotDefaults nodes(stream, "node"); - nodes.GetAttributeSet() - .AddAttribute("shape", "record"); - } - - { - // Default edge attributes: - DotDefaults edges(stream, "edge"); - edges.GetAttributeSet() - .AddAttribute("fontsize", 8) - .AddAttribute("fontcolor", "blue") - .AddAttribute("fontname", "arial-bold"); - } - - // First declares the nodes. - for (auto&& layer : m_Layers) - { - DotNode node(stream, layer->GetGuid(), GetLayerTypeAsCString(layer->GetType())); - // Extracts the layer parameters. - ParameterStringifyFunction extractParams = [&node](const std::string & name, const std::string & value){ - node.GetContents().AddContent(name + " : " + value); - }; - layer->SerializeLayerParameters(extractParams); - } - - // Second declares the edges. - for (auto&& layer : m_Layers) - { - LayerGuid toId = layer->GetGuid(); - - for (unsigned int i=0;i<layer->GetNumInputSlots(); i++) - { - OutputSlot* outputSlot = static_cast<OutputSlot*>(layer->GetInputSlot(i).GetConnection()); - LayerGuid fromId = outputSlot->GetOwningLayer().GetGuid(); - DotEdge edge(stream, fromId, toId); - - // Now print the tensor shape on the edge. - { - // Constructs the label attribute with HTML markup. - std::stringstream ss; - ss << "< " << outputSlot->GetTensorInfo().GetShape() << " >"; - edge.GetAttributeSet().AddAttribute("label", ss); - } - } - } - } - - if (stream.bad()) - { - return Status::Failure; - } - return Status::Success; -} - -Status Graph::AllocateDynamicBuffers() -{ - // Layers must be sorted in topological order - BOOST_ASSERT(m_LayersInOrder); - - std::unordered_set<const ITensorHandle*> preallocatedTensors; - std::unordered_map<const ITensorHandle*, unsigned int> handleReferenceCounts; - - // Finds the first TensorHandle ancestor of a SubTensorHandle. If the ITensorHandle provided - // is a TensorHandle, the function just returns it - auto TraceSubTensorHandleAncestry = [](ITensorHandle* const subTensorHandle) - { - ITensorHandle* ancestor = subTensorHandle; - while (ancestor && ancestor->GetParent()) - { - ancestor = ancestor->GetParent(); - } - return ancestor; - }; - - // Checks whether a TensorHandle has been pre-allocated - auto IsPreallocated = [&](ITensorHandle* const tensorHandle) - { - return tensorHandle && preallocatedTensors.find(tensorHandle) != preallocatedTensors.end(); - }; - - // Constant tensor handles need to last from the beginning of execution till the end, - // therefore we pre-allocate them upfront - for (auto&& layer : m_Layers) - { - if (layer->GetType() == LayerType::Constant) - { - for (auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot) - { - ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()); - - if (tensorHandle && !IsPreallocated(tensorHandle)) - { - tensorHandle->Allocate(); - preallocatedTensors.insert(tensorHandle); - } - } - } - } - - // Iterate over the network in topological order - for (auto&& layer : m_Layers) - { - // Count the amount of times each output slot references a certain buffer (ITensorHandle). - // The first time we encounter a new tensor handle, we start managing its lifetime. - for (auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot) - { - ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()); - - if (tensorHandle && !IsPreallocated(tensorHandle)) - { - unsigned int numConnections = slot->GetNumConnections(); - if (handleReferenceCounts.find(tensorHandle) == handleReferenceCounts.end()) - { - handleReferenceCounts[tensorHandle] = numConnections; - tensorHandle->Manage(); - if (handleReferenceCounts[tensorHandle] == 0u) - { - // if nobody consumes this tensor we call Allocate() - tensorHandle->Allocate(); - } - } - else - { - handleReferenceCounts[tensorHandle] += numConnections; - } - } - } - - // Loop through the input slots in the same layer and decrement the reference counter associated - // to each tensor handle we encounter. Once it reaches zero, we end the lifetime of the tensor handle - for (auto&& slot = layer->BeginInputSlots(); slot != layer->EndInputSlots(); ++slot) - { - ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry( - slot->GetConnectedOutputSlot()->GetOutputHandler().GetData()); - - if (tensorHandle && !IsPreallocated(tensorHandle)) - { - --handleReferenceCounts[tensorHandle]; - - if (handleReferenceCounts[tensorHandle] == 0u) - { - // Stop managing lifetime of tensor handle - tensorHandle->Allocate(); - handleReferenceCounts.erase(tensorHandle); - } - } - } - } - - return Status::Success; -} - -const Graph& Graph::TopologicalSort() const -{ - if (!m_LayersInOrder) - { - // Resets layer order. - for (auto&& it : m_Layers) - { - it->ResetPriority(); - } - - auto compareLayerPriority = [](const LayerList::value_type& layerA, const LayerList::value_type& layerB) - { - return layerA->GetPriority() < layerB->GetPriority(); - }; - - m_Layers.sort(compareLayerPriority); - - m_LayersInOrder = true; - } - - return *this; -} - -void Graph::AddCompatibilityLayers(std::map<BackendId, std::unique_ptr<IBackendInternal>>& backends, - TensorHandleFactoryRegistry& registry) -{ - // Returns true if the given layer could potentially need an intermediate copy/import layer (depending on its - // connections to other layers). - auto MayNeedCompatibilityLayer = [](const Layer& layer) - { - // All layers should have been associated with a valid compute device at this point. - BOOST_ASSERT(layer.GetBackendId() != Compute::Undefined); - // Does not need another compatibility layer if a copy or import layer is already present. - return layer.GetType() != LayerType::MemCopy && - layer.GetType() != LayerType::MemImport; - }; - - auto IsCompatibilityStrategy = [](EdgeStrategy strategy) - { - return strategy == EdgeStrategy::CopyToTarget || - strategy == EdgeStrategy::ExportToTarget; - }; - - ForEachLayer([this, &backends, ®istry, MayNeedCompatibilityLayer, IsCompatibilityStrategy](Layer* srcLayer) - { - BOOST_ASSERT(srcLayer); - - if (!MayNeedCompatibilityLayer(*srcLayer)) - { - // The current layer does not need copy layers, move to the next one - return; - } - - const std::vector<OutputSlot>& srcOutputSlots = srcLayer->GetOutputSlots(); - for (unsigned int srcOutputIndex = 0; srcOutputIndex < srcOutputSlots.size(); srcOutputIndex++) - { - OutputSlot& srcOutputSlot = srcLayer->GetOutputSlot(srcOutputIndex); - const std::vector<InputSlot*> srcConnections = srcOutputSlot.GetConnections(); - const std::vector<EdgeStrategy> srcEdgeStrategies = srcOutputSlot.GetEdgeStrategies(); - for (unsigned int srcConnectionIndex = 0; srcConnectionIndex < srcConnections.size(); srcConnectionIndex++) - { - InputSlot* dstInputSlot = srcConnections[srcConnectionIndex]; - BOOST_ASSERT(dstInputSlot); - - EdgeStrategy strategy = srcEdgeStrategies[srcConnectionIndex]; - BOOST_ASSERT_MSG(strategy != EdgeStrategy::Undefined, - "Undefined memory strategy found while adding copy layers for compatibility"); - - const Layer& dstLayer = dstInputSlot->GetOwningLayer(); - if (MayNeedCompatibilityLayer(dstLayer) && - IsCompatibilityStrategy(strategy)) - { - // A copy layer is needed in between the source and destination layers. - // Record the operation rather than attempting to modify the graph as we go. - // (invalidating iterators) - const std::string compLayerName = boost::str(boost::format("[ %1% (%2%) -> %3% (%4%) ]") - % srcLayer->GetName() - % srcOutputIndex - % dstLayer.GetName() - % dstInputSlot->GetSlotIndex()); - - Layer* compLayer = nullptr; - if (strategy == EdgeStrategy::CopyToTarget) - { - compLayer = InsertNewLayer<MemCopyLayer>(*dstInputSlot, compLayerName.c_str()); - } - else - { - BOOST_ASSERT_MSG(strategy == EdgeStrategy::ExportToTarget, "Invalid edge strategy found."); - compLayer = InsertNewLayer<MemImportLayer>(*dstInputSlot, compLayerName.c_str()); - } - - compLayer->SetBackendId(dstLayer.GetBackendId()); - - OutputSlot& compOutputSlot = compLayer->GetOutputSlot(0); - auto backendIt = backends.find(dstLayer.GetBackendId()); - if (backendIt != backends.end() && - backendIt->second && - backendIt->second->SupportsTensorAllocatorAPI()) - { - auto backend = backendIt->second.get(); - auto tensorHandleFactoryIds = backend->GetHandleFactoryPreferences(); - bool found = false; - - for (auto preference : tensorHandleFactoryIds) - { - auto factory = registry.GetFactory(preference); - if (factory) - { - auto srcPref = srcOutputSlot.GetTensorHandleFactoryId(); - auto srcFactory = registry.GetFactory(srcPref); - - if (srcFactory) - { - bool canExportImport = - (factory->GetImportFlags() & srcFactory->GetExportFlags()) != 0; - - if (factory->SupportsMapUnmap() || canExportImport) - { - compOutputSlot.SetTensorHandleFactory(preference); - found = true; - break; - } - } - } - } - - if (!found) - { - compOutputSlot.SetTensorHandleFactory(ITensorHandleFactory::LegacyFactoryId); - } - } - else - { - compOutputSlot.SetTensorHandleFactory(ITensorHandleFactory::LegacyFactoryId); - } - - // The output strategy of a compatibility layer is always DirectCompatibility. - compOutputSlot.SetEdgeStrategy(0, EdgeStrategy::DirectCompatibility); - - // Recalculate the connection index on the previous layer as we have just inserted into it. - const std::vector<InputSlot*>& newSourceConnections = srcOutputSlot.GetConnections(); - long newSrcConnectionIndex = std::distance(newSourceConnections.begin(), - std::find(newSourceConnections.begin(), - newSourceConnections.end(), - &compLayer->GetInputSlot(0))); - - // The input strategy of a compatibility layer is always DirectCompatibilty. - srcOutputSlot.SetEdgeStrategy(boost::numeric_cast<unsigned int>(newSrcConnectionIndex), - EdgeStrategy::DirectCompatibility); - } - } - } - }); -} - -void Graph::SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substituteLayer) -{ - BOOST_ASSERT(substituteLayer != nullptr); - - ReplaceSubgraphConnections(subgraph, substituteLayer); - EraseSubgraphLayers(subgraph); -} - -void Graph::SubstituteSubgraph(SubgraphView& subgraph, const SubgraphView& substituteSubgraph) -{ - // Look through each layer in the new subgraph and add any that are not already a member of this graph - substituteSubgraph.ForEachLayer([this](Layer* layer) - { - if (std::find(std::begin(m_Layers), std::end(m_Layers), layer) == std::end(m_Layers)) - { - layer->Reparent(*this, m_Layers.end()); - m_LayersInOrder = false; - } - }); - - ReplaceSubgraphConnections(subgraph, substituteSubgraph); - EraseSubgraphLayers(subgraph); - TopologicalSort(); -} - -void Graph::ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer) -{ - BOOST_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) -{ - BOOST_ASSERT_MSG(!substituteSubgraph.GetLayers().empty(), "New sub-graph used for substitution must not be empty"); - - const SubgraphView::Layers& substituteSubgraphLayers = substituteSubgraph.GetLayers(); - std::for_each(substituteSubgraphLayers.begin(), substituteSubgraphLayers.end(), [&](Layer* layer) - { - boost::ignore_unused(layer); - BOOST_ASSERT_MSG(std::find(m_Layers.begin(), m_Layers.end(), layer) != m_Layers.end(), - "Substitute layer is not a member of graph"); - }); - - const SubgraphView::InputSlots& subgraphInputSlots = subgraph.GetInputSlots(); - const SubgraphView::OutputSlots& subgraphOutputSlots = subgraph.GetOutputSlots(); - - unsigned int subgraphNumInputSlots = boost::numeric_cast<unsigned int>(subgraphInputSlots.size()); - unsigned int subgraphNumOutputSlots = boost::numeric_cast<unsigned int>(subgraphOutputSlots.size()); - - const SubgraphView::InputSlots& substituteSubgraphInputSlots = substituteSubgraph.GetInputSlots(); - const SubgraphView::OutputSlots& substituteSubgraphOutputSlots = substituteSubgraph.GetOutputSlots(); - - BOOST_ASSERT(subgraphNumInputSlots == substituteSubgraphInputSlots.size()); - BOOST_ASSERT(subgraphNumOutputSlots == substituteSubgraphOutputSlots.size()); - - // Disconnect the sub-graph and replace it with the substitute sub-graph - - // Step 1: process input slots - for (unsigned int inputSlotIdx = 0; inputSlotIdx < subgraphNumInputSlots; ++inputSlotIdx) - { - InputSlot* subgraphInputSlot = subgraphInputSlots.at(inputSlotIdx); - BOOST_ASSERT(subgraphInputSlot); - - IOutputSlot* connectedOutputSlot = subgraphInputSlot->GetConnection(); - BOOST_ASSERT(connectedOutputSlot); - connectedOutputSlot->Disconnect(*subgraphInputSlot); - - IInputSlot* substituteInputSlot = substituteSubgraphInputSlots.at(inputSlotIdx); - BOOST_ASSERT(substituteInputSlot); - connectedOutputSlot->Connect(*substituteInputSlot); - } - - // Step 2: process output slots - for(unsigned int outputSlotIdx = 0; outputSlotIdx < subgraphNumOutputSlots; ++outputSlotIdx) - { - OutputSlot* subgraphOutputSlot = subgraphOutputSlots.at(outputSlotIdx); - BOOST_ASSERT(subgraphOutputSlot); - - OutputSlot* substituteOutputSlot = substituteSubgraphOutputSlots.at(outputSlotIdx); - BOOST_ASSERT(substituteOutputSlot); - subgraphOutputSlot->MoveAllConnections(*substituteOutputSlot); - } -} - -void Graph::EraseSubgraphLayers(SubgraphView &subgraph) -{ - for (auto layer : subgraph.GetLayers()) - { - EraseLayer(layer); - } - subgraph.Clear(); -} - -void Graph::InferTensorInfos() -{ - for (auto&& layer : TopologicalSort()) - { - for (auto&& input : layer->GetInputSlots()) - { - const IOutputSlot* source = input.GetConnectedOutputSlot(); - if (source == NULL) - { - std::ostringstream message; - message << "Input not connected on " - << GetLayerTypeAsCString(layer->GetType()) - << " layer \"" - << layer->GetName() - << "\""; - throw LayerValidationException(message.str()); - } - - if (!source->IsTensorInfoSet()) - { - throw LayerValidationException("All inputs must have the TensorInfo set at this point."); - } - } - layer->ValidateTensorShapesFromInputs(); - } -} - -} // namespace armnn |