diff options
Diffstat (limited to 'src/backends')
-rw-r--r-- | src/backends/aclCommon/ArmComputeUtils.hpp | 21 | ||||
-rw-r--r-- | src/backends/backendsCommon/LayerSupportBase.cpp | 8 | ||||
-rw-r--r-- | src/backends/backendsCommon/LayerSupportBase.hpp | 6 | ||||
-rw-r--r-- | src/backends/backendsCommon/WorkloadFactory.cpp | 13 | ||||
-rw-r--r-- | src/backends/cl/ClLayerSupport.cpp | 13 | ||||
-rw-r--r-- | src/backends/cl/ClLayerSupport.hpp | 6 | ||||
-rw-r--r-- | src/backends/neon/NeonLayerSupport.cpp | 34 | ||||
-rw-r--r-- | src/backends/neon/NeonLayerSupport.hpp | 6 | ||||
-rw-r--r-- | src/backends/neon/backend.mk | 1 | ||||
-rw-r--r-- | src/backends/neon/workloads/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/backends/neon/workloads/NeonSplitterWorkload.cpp | 112 | ||||
-rw-r--r-- | src/backends/neon/workloads/NeonSplitterWorkload.hpp | 18 | ||||
-rw-r--r-- | src/backends/reference/RefLayerSupport.cpp | 13 | ||||
-rw-r--r-- | src/backends/reference/RefLayerSupport.hpp | 6 |
14 files changed, 253 insertions, 5 deletions
diff --git a/src/backends/aclCommon/ArmComputeUtils.hpp b/src/backends/aclCommon/ArmComputeUtils.hpp index b4673f7b31..5b8f983ecc 100644 --- a/src/backends/aclCommon/ArmComputeUtils.hpp +++ b/src/backends/aclCommon/ArmComputeUtils.hpp @@ -9,6 +9,8 @@ #include <arm_compute/core/Types.h> +#include <boost/assert.hpp> + namespace armnn { @@ -130,4 +132,23 @@ inline unsigned int ComputeSoftmaxAclAxis(const armnn::TensorInfo& tensor) return dim - 1; } +inline std::set<unsigned int> ComputeSplitAxis(const armnn::SplitterDescriptor& desc, const TensorShape& input) +{ + unsigned int numSplit = desc.GetNumViews(); + unsigned int numDimensions = desc.GetNumDimensions(); + std::set<unsigned int> splitAxis; + + for (unsigned int i = 0; i < numSplit; ++i) + { + for (unsigned int dimIdx = 0; dimIdx < numDimensions; ++dimIdx) + { + if (desc.GetViewSizes(i)[dimIdx] != input[dimIdx]) + { + splitAxis.insert(dimIdx); + } + } + } + return splitAxis; +} + } // namespace armnn diff --git a/src/backends/backendsCommon/LayerSupportBase.cpp b/src/backends/backendsCommon/LayerSupportBase.cpp index 7760c079ac..9fcb496ba3 100644 --- a/src/backends/backendsCommon/LayerSupportBase.cpp +++ b/src/backends/backendsCommon/LayerSupportBase.cpp @@ -401,6 +401,14 @@ bool LayerSupportBase::IsSplitterSupported(const TensorInfo& input, return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); } +bool LayerSupportBase::IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported) const +{ + return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); +} + bool LayerSupportBase::IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, diff --git a/src/backends/backendsCommon/LayerSupportBase.hpp b/src/backends/backendsCommon/LayerSupportBase.hpp index 88d5792819..75527584ed 100644 --- a/src/backends/backendsCommon/LayerSupportBase.hpp +++ b/src/backends/backendsCommon/LayerSupportBase.hpp @@ -247,10 +247,16 @@ public: const SpaceToBatchNdDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + ARMNN_DEPRECATED_MSG("Use IsSplitterSupported with outputs instead") bool IsSplitterSupported(const TensorInfo& input, const ViewsDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp index 9679c35acb..0490a94864 100644 --- a/src/backends/backendsCommon/WorkloadFactory.cpp +++ b/src/backends/backendsCommon/WorkloadFactory.cpp @@ -703,7 +703,20 @@ bool IWorkloadFactory::IsLayerSupported(const BackendId& backendId, { auto cLayer = boost::polymorphic_downcast<const SplitterLayer*>(&layer); const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); + + // Get vector of all outputs. + auto getTensorInfo = [&dataType](const OutputSlot& slot) + { + return OverrideDataType(slot.GetTensorInfo(), dataType); + }; + auto beginI = boost::make_transform_iterator(layer.GetOutputSlots().begin(), getTensorInfo); + auto endI = boost::make_transform_iterator(layer.GetOutputSlots().end(), getTensorInfo); + std::vector<TensorInfo> outputs(beginI, endI); + + const std::vector<std::reference_wrapper<TensorInfo>> outputPtrs(outputs.begin(), outputs.end()); + result = layerSupportObject->IsSplitterSupported(OverrideDataType(input, dataType), + outputPtrs, cLayer->GetParameters(), reason); break; diff --git a/src/backends/cl/ClLayerSupport.cpp b/src/backends/cl/ClLayerSupport.cpp index 2ce5179045..21d191ab2c 100644 --- a/src/backends/cl/ClLayerSupport.cpp +++ b/src/backends/cl/ClLayerSupport.cpp @@ -607,6 +607,19 @@ bool ClLayerSupport::IsSplitterSupported(const TensorInfo& input, &TrueFunc<>); } +bool ClLayerSupport::IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported) const +{ + ignore_unused(descriptor); + ignore_unused(outputs); + return IsSupportedForDataTypeCl(reasonIfUnsupported, + input.GetDataType(), + &TrueFunc<>, + &TrueFunc<>); +} + bool ClLayerSupport::IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, diff --git a/src/backends/cl/ClLayerSupport.hpp b/src/backends/cl/ClLayerSupport.hpp index b634d46768..fca0bfd352 100644 --- a/src/backends/cl/ClLayerSupport.hpp +++ b/src/backends/cl/ClLayerSupport.hpp @@ -200,10 +200,16 @@ public: const SpaceToBatchNdDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + ARMNN_DEPRECATED_MSG("Use IsSplitterSupported with outputs instead") bool IsSplitterSupported(const TensorInfo& input, const ViewsDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, diff --git a/src/backends/neon/NeonLayerSupport.cpp b/src/backends/neon/NeonLayerSupport.cpp index f4599ff8e4..fd9aac5bc5 100644 --- a/src/backends/neon/NeonLayerSupport.cpp +++ b/src/backends/neon/NeonLayerSupport.cpp @@ -17,6 +17,7 @@ #include <boost/core/ignore_unused.hpp> #if defined(ARMCOMPUTENEON_ENABLED) +#include <aclCommon/ArmComputeUtils.hpp> #include "workloads/NeonAdditionWorkload.hpp" #include "workloads/NeonActivationWorkload.hpp" #include "workloads/NeonBatchNormalizationWorkload.hpp" @@ -36,6 +37,7 @@ #include "workloads/NeonPooling2dWorkload.hpp" #include "workloads/NeonResizeBilinearWorkload.hpp" #include "workloads/NeonSoftmaxBaseWorkload.hpp" +#include "workloads/NeonSplitterWorkload.hpp" #include "workloads/NeonSubtractionWorkload.hpp" #endif @@ -478,6 +480,38 @@ bool NeonLayerSupport::IsSplitterSupported(const TensorInfo& input, &TrueFunc<>); } +bool NeonLayerSupport::IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported) const +{ +#if defined(ARMCOMPUTENEON_ENABLED) + // Split along the last dimension, cannot use sub-tensors + // as width and height of the sub-tensors do not match + // the width and height of the parent tensor + // in case of input with more than 2D. + std::set<unsigned int> splitAxis = ComputeSplitAxis(descriptor, input.GetShape()); + if (descriptor.GetNumDimensions() > 2 && splitAxis.size() == 1 && + *splitAxis.begin() == descriptor.GetNumDimensions() - 1 ) + { + FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSplitterWorkloadValidate, + reasonIfUnsupported, + input, + outputs, + *splitAxis.begin()); + } +#endif + for (auto output : outputs) + { + if (!input.IsTypeSpaceMatch(output)) // Cannot use sub-tensors if the types are not same space + { + SetValueChecked(reasonIfUnsupported, "Neon Splitter: Types and quantization parameters must match."); + return false; + } + } + return true; +} + bool NeonLayerSupport::IsSubtractionSupported(const TensorInfo& input0, const TensorInfo& input1, const TensorInfo& output, diff --git a/src/backends/neon/NeonLayerSupport.hpp b/src/backends/neon/NeonLayerSupport.hpp index 8312bb977a..5e8e0bdbed 100644 --- a/src/backends/neon/NeonLayerSupport.hpp +++ b/src/backends/neon/NeonLayerSupport.hpp @@ -158,10 +158,16 @@ public: const SoftmaxDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + ARMNN_DEPRECATED_MSG("Use IsSplitterSupported with outputs instead") bool IsSplitterSupported(const TensorInfo& input, const ViewsDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsSubtractionSupported(const TensorInfo& input0, const TensorInfo& input1, const TensorInfo& output, diff --git a/src/backends/neon/backend.mk b/src/backends/neon/backend.mk index 6824879ac9..6931bd7325 100644 --- a/src/backends/neon/backend.mk +++ b/src/backends/neon/backend.mk @@ -46,6 +46,7 @@ BACKEND_SOURCES := \ workloads/NeonSoftmaxBaseWorkload.cpp \ workloads/NeonSoftmaxFloatWorkload.cpp \ workloads/NeonSoftmaxUint8Workload.cpp \ + workloads/NeonSplitterWorkload.cpp \ workloads/NeonSubtractionWorkload.cpp else diff --git a/src/backends/neon/workloads/CMakeLists.txt b/src/backends/neon/workloads/CMakeLists.txt index f1c773dc4c..8b2ad63f45 100644 --- a/src/backends/neon/workloads/CMakeLists.txt +++ b/src/backends/neon/workloads/CMakeLists.txt @@ -58,6 +58,7 @@ list(APPEND armnnNeonBackendWorkloads_sources NeonSoftmaxFloatWorkload.hpp NeonSoftmaxUint8Workload.cpp NeonSoftmaxUint8Workload.hpp + NeonSplitterWorkload.cpp NeonSplitterWorkload.hpp NeonSubtractionWorkload.cpp NeonSubtractionWorkload.hpp diff --git a/src/backends/neon/workloads/NeonSplitterWorkload.cpp b/src/backends/neon/workloads/NeonSplitterWorkload.cpp new file mode 100644 index 0000000000..bf35939127 --- /dev/null +++ b/src/backends/neon/workloads/NeonSplitterWorkload.cpp @@ -0,0 +1,112 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "NeonSplitterWorkload.hpp" + +#include "NeonWorkloadUtils.hpp" + +#include <aclCommon/ArmComputeTensorUtils.hpp> +#include <aclCommon/ArmComputeUtils.hpp> +#include <backendsCommon/CpuTensorHandle.hpp> +#include <neon/NeonTensorHandle.hpp> + + +namespace armnn +{ + +using namespace armcomputetensorutils; + +namespace +{ +unsigned int CalcAclAxis(unsigned int numDimensions, unsigned int splitAxis) +{ + return (numDimensions - splitAxis) - 1; +} + +} //namespace + +arm_compute::Status NeonSplitterWorkloadValidate(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + unsigned int splitAxis) +{ + const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input); + + size_t numOutputs = outputs.size(); + + std::vector<arm_compute::TensorInfo> aclOutputs; + aclOutputs.reserve(numOutputs); + + std::vector<arm_compute::ITensorInfo*> aclOutputPtr; + aclOutputPtr.reserve(numOutputs); + + for (size_t i = 0u; i < outputs.size(); ++i) + { + aclOutputs.emplace_back(BuildArmComputeTensorInfo(outputs[i])); + aclOutputPtr.emplace_back(&aclOutputs.back()); + } + + unsigned int aclAxis = CalcAclAxis(input.GetNumDimensions(), splitAxis); + return arm_compute::NESplit::validate(&aclInputInfo, aclOutputPtr, aclAxis); +} + +NeonSplitterWorkload::NeonSplitterWorkload(const SplitterQueueDescriptor& descriptor, const WorkloadInfo& info) + : BaseWorkload<SplitterQueueDescriptor>(descriptor, info) +{ + bool allOutputsAreSubtensors = true; + + // Check that all outputs are sub-tensors + for (auto output : m_Data.m_Outputs) + { + if (output && !output->GetParent()) + { + // Non sub-tensor input found so we need to execute the split function + allOutputsAreSubtensors = false; + break; + } + } + + if (allOutputsAreSubtensors) + { + // Can skip configuring the split function since it's not executed + return; + } + + arm_compute::ITensor& input = boost::polymorphic_downcast<INeonTensorHandle*>(m_Data.m_Inputs[0])->GetTensor(); + + std::vector<arm_compute::ITensor *> aclOutputs; + for (auto output : m_Data.m_Outputs) + { + arm_compute::ITensor& aclOutput = boost::polymorphic_pointer_downcast<INeonTensorHandle>(output)->GetTensor(); + aclOutputs.emplace_back(&aclOutput); + } + + // Create the layer function + m_Layer.reset(new arm_compute::NESplit()); + + // Configure input and output tensors + std::set<unsigned int> splitAxis = ComputeSplitAxis(descriptor.m_Parameters, m_Data.m_Inputs[0]->GetShape()); + if (splitAxis.size() != 1) + { + throw InvalidArgumentException("Cannot derive split axis from SplitterDescriptor"); + } + + unsigned int aclAxis = CalcAclAxis(descriptor.m_Parameters.GetNumDimensions(), *splitAxis.begin()); + m_Layer->configure(&input, aclOutputs, aclAxis); + + // Prepare + m_Layer->prepare(); +} + +void NeonSplitterWorkload::Execute() const +{ + if (m_Layer) + { + ARMNN_SCOPED_PROFILING_EVENT_NEON("NeonSplitterWorkload_Execute"); + m_Layer->run(); + } +} + +} //namespace armnn + diff --git a/src/backends/neon/workloads/NeonSplitterWorkload.hpp b/src/backends/neon/workloads/NeonSplitterWorkload.hpp index 2a7ee193d0..f9025663ca 100644 --- a/src/backends/neon/workloads/NeonSplitterWorkload.hpp +++ b/src/backends/neon/workloads/NeonSplitterWorkload.hpp @@ -7,18 +7,26 @@ #include <backendsCommon/Workload.hpp> +#include <arm_compute/runtime/NEON/NEFunctions.h> + +#include <functional> + namespace armnn { +arm_compute::Status NeonSplitterWorkloadValidate(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + unsigned int splitAxis); + class NeonSplitterWorkload : public BaseWorkload<SplitterQueueDescriptor> { public: - using BaseWorkload<SplitterQueueDescriptor>::BaseWorkload; + NeonSplitterWorkload(const SplitterQueueDescriptor& descriptor, const WorkloadInfo& info); + + void Execute() const override; - virtual void Execute() const override - { - // With subtensors, splitter is a no-op. - } +private: + mutable std::unique_ptr<arm_compute::NESplit> m_Layer; }; } //namespace armnn diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp index 7beff72dad..6ad6816474 100644 --- a/src/backends/reference/RefLayerSupport.cpp +++ b/src/backends/reference/RefLayerSupport.cpp @@ -998,6 +998,19 @@ bool RefLayerSupport::IsSplitterSupported(const TensorInfo& input, &TrueFunc<>); } +bool RefLayerSupport::IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported) const +{ + ignore_unused(descriptor); + ignore_unused(outputs); + return IsSupportedForDataTypeRef(reasonIfUnsupported, + input.GetDataType(), + &TrueFunc<>, + &TrueFunc<>); +} + bool RefLayerSupport::IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, diff --git a/src/backends/reference/RefLayerSupport.hpp b/src/backends/reference/RefLayerSupport.hpp index a4ae01e403..944061d5a6 100644 --- a/src/backends/reference/RefLayerSupport.hpp +++ b/src/backends/reference/RefLayerSupport.hpp @@ -236,10 +236,16 @@ public: const SpaceToBatchNdDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + ARMNN_DEPRECATED_MSG("Use IsSplitterSupported with outputs instead") bool IsSplitterSupported(const TensorInfo& input, const ViewsDescriptor& descriptor, Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsSplitterSupported(const TensorInfo& input, + const std::vector<std::reference_wrapper<TensorInfo>>& outputs, + const ViewsDescriptor& descriptor, + Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override; + bool IsStridedSliceSupported(const TensorInfo& input, const TensorInfo& output, const StridedSliceDescriptor& descriptor, |