From 6440ce89abb06e090d2b3cf91bafc14277072475 Mon Sep 17 00:00:00 2001 From: Tracy Narine Date: Wed, 20 Sep 2023 14:19:07 +0100 Subject: IVGCVSW-7504 Create a backend specific optimization to fuse ADD+MUL+Add+(Activation) in CpuAcc * Adding CpuAcc backend optimization to fuse add+mul+add into one layer * Tests added/enhanced * Also added optional extended parameter to Graph::Print() and throw macros that could be used in place of assert Signed-off-by: Tracy Narine Signed-off-by: Teresa Charlin Change-Id: I5f8d094b969a130d8c2c7b4da07426313a9fea76 --- src/backends/backendsCommon/SubgraphUtils.hpp | 160 ++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) (limited to 'src/backends/backendsCommon/SubgraphUtils.hpp') diff --git a/src/backends/backendsCommon/SubgraphUtils.hpp b/src/backends/backendsCommon/SubgraphUtils.hpp index 9f2cdba6ef..6a9e8f1b76 100644 --- a/src/backends/backendsCommon/SubgraphUtils.hpp +++ b/src/backends/backendsCommon/SubgraphUtils.hpp @@ -161,6 +161,53 @@ SubgraphView::IOutputSlots CreateIOutputsFrom(const std::vector SlotList; + +template +SubgraphView::IInputSlots CreateIInputsFromSlotLists(const std::vector& layers, + const std::vector& layersSlotLists) +{ + ARMNN_THROW_INVALIDARG_IF_FALSE(layersSlotLists.size() == layers.size()); + + SubgraphView::IInputSlots result; + + for (unsigned int layerIdx = 0; layerIdx < layers.size(); ++layerIdx) + { + const SlotList& slotList = layersSlotLists[layerIdx]; + for (unsigned int slotIdx = 0 ; slotIdx < layers[layerIdx]->GetNumInputSlots(); ++slotIdx) + { + if (std::find(slotList.begin(), slotList.end(), slotIdx) != slotList.end()) + { + result.push_back(&(layers[layerIdx]->GetInputSlot(slotIdx))); + } + } + } + return result; +} + +template +SubgraphView::IOutputSlots CreateIOutputsFromSlotLists(const std::vector& layers, + const std::vector& layersSlotLists) +{ + ARMNN_THROW_INVALIDARG_IF_FALSE(layersSlotLists.size() == layers.size()); + + SubgraphView::IOutputSlots result; + for (unsigned int layerIdx = 0; layerIdx < layers.size(); ++layerIdx) + { + const SlotList& slotList = layersSlotLists[layerIdx]; + for (unsigned int slotIdx = 0; slotIdx < layers[layerIdx]->GetNumOutputSlots(); ++slotIdx) + { + bool foundIt = std::find(slotList.begin(), slotList.end(), slotIdx) != slotList.end(); + if (foundIt) + { + result.push_back(&(layers[layerIdx]->GetOutputSlot(slotIdx))); + } + } + } + return result; +} } inline bool IsNCHW(armnn::Layer& layer) @@ -308,4 +355,117 @@ LayerType* FoldPadIntoAveragePool2d(OptimizationViews& optimizationViews, return replacementLayer; } +// +// Layer sequence detection such as add + mul + add ( + optional activation ) +// + +inline bool IsSequenceLayerType(Layer& layer, LayerType type) +{ + return layer.GetType() == type; +} + +inline bool IsSequenceLayerType(Layer& layer, BinaryOperation type) +{ + return (layer.GetType() == LayerType::ElementwiseBinary) && + (PolymorphicDowncast(&layer)->GetParameters().m_Operation == type); +} + +// Detect a layer sequence and activation if specified. The activation must be at the end of the sequence. +template +bool IsLayerSequence(Layer& currentLayer, + TYPE first, + TYPE second, + TYPE third, + Layer* layerList[4], + bool handleValidActivates, + const std::vector& validActivates) +{ + auto PreviousLayer = [](Layer& layer) + { + return &layer.GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer(); + }; + + auto NextLayer = [](Layer& layer) + { + return &layer.GetOutputSlot(0).GetConnection(0)->GetOwningLayer(); + }; + + auto LayerIncomingConnectionDataType = [](Layer& layer) + { + return layer.GetInputSlot(0).GetTensorInfo().GetDataType(); + }; + + bool result = false; + + // Match in reverse so there is only 1 connection to check + if (IsSequenceLayerType(currentLayer, third)) + { + // Save DataType of third layer + DataType dataType = LayerIncomingConnectionDataType(currentLayer); + + // Save third layer + layerList[2] = ¤tLayer; + + // Check the layers that proceed this one for the requested grouping + Layer *prevLayer = PreviousLayer(currentLayer); + if (prevLayer && IsSequenceLayerType(*prevLayer, second)) + { + bool dataTypesMatch = (dataType == LayerIncomingConnectionDataType(*prevLayer)); + if (! dataTypesMatch) + { + return result; + } + + layerList[1] = prevLayer; + prevLayer = PreviousLayer(*prevLayer); + if (prevLayer && IsSequenceLayerType(*prevLayer, first)) + { + dataTypesMatch = (dataType == LayerIncomingConnectionDataType(*prevLayer)); + if (! dataTypesMatch) + { + return result; + } + + layerList[0] = prevLayer; + + // Detected the first 3 layers if we get to this point so now + // check to see if we have a valid activation. If there is no activation + // then the sequence still matches. + if (handleValidActivates) + { + Layer *nextLayer = NextLayer(currentLayer); + if (nextLayer) + { + if (IsSequenceLayerType(*nextLayer, LayerType::Activation)) + { + // This layer is an activation, so it must be a valid type for the sequence + ActivationFunction activationFunction = + PolymorphicDowncast(nextLayer)->GetParameters().m_Function; + long count = std::count(validActivates.cbegin(), + validActivates.cend(), + activationFunction); + if (count > 0) + { + layerList[3] = nextLayer; + result = true; + } + } + else + { + // Next layer is not an activation so sequence still matches + result = true; + } + } + } + else + { + result = true; + } + } + } + } + + return result; +} + } // namespace armnn -- cgit v1.2.1