2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
6 #include "LayersFwd.hpp"
8 #include <armnn/Utils.hpp>
9 #include <armnn/TypesUtils.hpp>
11 #include <boost/polymorphic_cast.hpp>
12 #include <boost/log/trivial.hpp>
13 #include <boost/assert.hpp>
14 #include <boost/format.hpp>
16 #include <unordered_map>
17 #include <DotSerializer.hpp>
24 Graph::Graph(const Graph& other)
25 : m_LayersInOrder(other.m_LayersInOrder)
27 std::unordered_map<const Layer*, Layer*> otherToClonedMap;
29 for (auto&& otherLayer : other.m_Layers)
31 Layer* const layer = otherLayer->Clone(*this);
32 otherToClonedMap.emplace(otherLayer, layer);
35 // Copies slot connections.
36 for (auto&& otherLayer : other.m_Layers)
38 Layer* const thisLayer = otherToClonedMap[otherLayer];
40 auto outputSlot = thisLayer->BeginOutputSlots();
41 for (auto&& otherOutputSlot : otherLayer->GetOutputSlots())
43 for (auto&& otherInputSlot : otherOutputSlot.GetConnections())
45 const Layer& otherTgtLayer = otherInputSlot->GetOwningLayer();
46 Layer* const thisTgtLayer = otherToClonedMap[&otherTgtLayer];
48 InputSlot& inputSlot = thisTgtLayer->GetInputSlot(otherInputSlot->GetSlotIndex());
49 outputSlot->Connect(inputSlot);
51 outputSlot->SetTensorInfo(otherOutputSlot.GetTensorInfo());
57 Status Graph::Print() const
61 BOOST_LOG_TRIVIAL(info) << "\n Graph is empty.\n";
62 return Status::Success;
64 BOOST_LOG_TRIVIAL(info) << "\n";
65 BOOST_LOG_TRIVIAL(info) << "Walking Pattern: \n";
67 for (auto&& it : TopologicalSort())
69 BOOST_LOG_TRIVIAL(info) << it->GetName() << ":" << GetLayerTypeAsCString(it->GetType())
70 << ":" << GetComputeDeviceAsCString(it->GetComputeDevice());
72 BOOST_LOG_TRIVIAL(info) << "\n\n";
74 return Status::Success;
77 Status Graph::SerializeToDot(std::ostream& stream)
80 DotGraph graph(stream, "Optimized");
83 // Default node attributes:
84 DotDefaults nodes(stream, "node");
85 nodes.GetAttributeSet()
86 .AddAttribute("shape", "record");
90 // Default edge attributes:
91 DotDefaults edges(stream, "edge");
92 edges.GetAttributeSet()
93 .AddAttribute("fontsize", 8)
94 .AddAttribute("fontcolor", "blue")
95 .AddAttribute("fontname", "arial-bold");
98 // First declares the nodes.
99 for (auto&& layer : m_Layers)
101 DotNode node(stream, layer->GetGuid(), GetLayerTypeAsCString(layer->GetType()));
102 // Extracts the layer parameters.
103 ParameterStringifyFunction extractParams = [&node](const std::string & name, const std::string & value){
104 node.GetContents().AddContent(name + " : " + value);
106 layer->SerializeLayerParameters(extractParams);
109 // Second declares the edges.
110 for (auto&& layer : m_Layers)
112 LayerGuid toId = layer->GetGuid();
114 for (unsigned int i=0;i<layer->GetNumInputSlots(); i++)
116 OutputSlot* outputSlot = static_cast<OutputSlot*>(layer->GetInputSlot(i).GetConnection());
117 LayerGuid fromId = outputSlot->GetOwningLayer().GetGuid();
118 DotEdge edge(stream, fromId, toId);
120 // Now print the tensor shape on the edge.
122 // Constructs the label attribute with HTML markup.
123 std::stringstream ss;
124 ss << "< " << outputSlot->GetTensorInfo().GetShape() << " >";
125 edge.GetAttributeSet().AddAttribute("label", ss);
133 return Status::Failure;
135 return Status::Success;
138 Status Graph::AllocateDynamicBuffers()
140 // Layers must be sorted in topological order
141 BOOST_ASSERT(m_LayersInOrder);
143 std::unordered_set<const ITensorHandle*> preallocatedTensors;
144 std::unordered_map<const ITensorHandle*, unsigned int> handleReferenceCounts;
146 // Finds the first TensorHandle ancestor of a SubTensorHandle. If the ITensorHandle provided
147 // is a TensorHandle, the function just returns it
148 auto TraceSubTensorHandleAncestry = [](ITensorHandle* const subTensorHandle)
150 ITensorHandle* ancestor = subTensorHandle;
151 while (ancestor && ancestor->GetParent())
153 ancestor = ancestor->GetParent();
158 // Checks whether a TensorHandle has been pre-allocated
159 auto IsPreallocated = [&](ITensorHandle* const tensorHandle)
161 return tensorHandle && preallocatedTensors.find(tensorHandle) != preallocatedTensors.end();
164 // Constant tensor handles need to last from the beginning of execution till the end,
165 // therefore we pre-allocate them upfront
166 for (auto&& layer : m_Layers)
168 if (layer->GetType() == LayerType::Constant)
170 for (auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot)
172 ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData());
174 if (tensorHandle && !IsPreallocated(tensorHandle))
176 tensorHandle->Allocate();
177 preallocatedTensors.insert(tensorHandle);
183 // Iterate over the network in topological order
184 for (auto&& layer : m_Layers)
186 // Count the amount of times each output slot references a certain buffer (ITensorHandle).
187 // The first time we encounter a new tensor handle, we start managing its lifetime.
188 for (auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot)
190 ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData());
192 if (tensorHandle && !IsPreallocated(tensorHandle))
194 unsigned int numConnections = slot->GetNumConnections();
195 if (handleReferenceCounts.find(tensorHandle) == handleReferenceCounts.end())
197 handleReferenceCounts[tensorHandle] = numConnections;
198 tensorHandle->Manage();
202 handleReferenceCounts[tensorHandle] += numConnections;
207 // Loop through the input slots in the same layer and decrement the reference counter associated
208 // to each tensor handle we encounter. Once it reaches zero, we end the lifetime of the tensor handle
209 for (auto&& slot = layer->BeginInputSlots(); slot != layer->EndInputSlots(); ++slot)
211 ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(
212 slot->GetConnectedOutputSlot()->GetOutputHandler().GetData());
214 if (tensorHandle && !IsPreallocated(tensorHandle))
216 --handleReferenceCounts[tensorHandle];
218 if (handleReferenceCounts[tensorHandle] == 0u)
220 // Stop managing lifetime of tensor handle
221 tensorHandle->Allocate();
222 handleReferenceCounts.erase(tensorHandle);
228 return Status::Success;
231 const Graph& Graph::TopologicalSort() const
233 if (!m_LayersInOrder)
235 // Resets layer order.
236 for (auto&& it : m_Layers)
241 auto compareLayerPriority = [](const LayersList::value_type& layerA, const LayersList::value_type& layerB)
243 return layerA->GetPriority() < layerB->GetPriority();
246 m_Layers.sort(compareLayerPriority);
248 m_LayersInOrder = true;
254 void Graph::AddCopyLayers()
256 // Returns true if the given layer could potentially need an intermediate copy layer (depending on its
257 // connections to other layers). At the time of writing, copy layers will be inserted in the following situations:
258 // CPU -> CL (and viceversa)
259 // CPU -> Neon (and viceversa)
260 auto MayNeedCopyLayer = [](const Layer& layer)
262 // All layers should have been associated with a valid compute device at this point.
263 BOOST_ASSERT(layer.GetComputeDevice() != Compute::Undefined);
264 // Does not need another copy layer if a copy layer is already present.
265 return layer.GetType() != LayerType::MemCopy;
268 for (auto&& srcLayer : m_Layers)
270 if (MayNeedCopyLayer(*srcLayer))
272 unsigned int srcOutputIndex = 0;
273 for (auto&& srcOutput : srcLayer->GetOutputSlots())
275 std::vector<InputSlot*> connectionCopy = srcOutput.GetConnections();
276 for (auto&& dstInput : connectionCopy)
278 Layer& dstLayer = dstInput->GetOwningLayer();
279 if (MayNeedCopyLayer(dstLayer) && (dstLayer.GetComputeDevice() != srcLayer->GetComputeDevice()))
281 // A copy layer is needed in between the source and destination layers.
282 // Record the operation rather than attempting to modify the graph as we go.
283 // (invalidating iterators)
284 const std::string copyLayerName = boost::str(boost::format("[ %1% (%2%) -> %3% (%4%) ]")
285 % srcLayer->GetName()
288 % dstInput->GetSlotIndex());
290 MemCopyLayer* const copyLayer = InsertNewLayer<MemCopyLayer>(*dstInput, copyLayerName.c_str());
291 copyLayer->SetComputeDevice(dstLayer.GetComputeDevice());
300 void Graph::InferTensorInfos()
302 for (auto&& layer : TopologicalSort())
304 for (auto&& input : layer->GetInputSlots())
306 boost::ignore_unused(input);
307 BOOST_ASSERT_MSG(input.GetConnectedOutputSlot()->IsTensorInfoSet(),
308 "All inputs must have the TensorInfo set at this point.");
310 layer->ValidateTensorShapesFromInputs();