| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| |
| #include <doctest/doctest.h> |
| |
| #include <armnn/LayerVisitorBase.hpp> |
| |
| #include <armnn/backends/IBackendContext.hpp> |
| #include <armnn/backends/IBackendInternal.hpp> |
| #include <armnn/backends/IMemoryManager.hpp> |
| #include <armnn/backends/ITensorHandleFactory.hpp> |
| #include <backendsCommon/TensorHandleFactoryRegistry.hpp> |
| |
| #include <optimizations/Optimization.hpp> |
| |
| #include <Network.hpp> |
| |
| #include <armnn/utility/IgnoreUnused.hpp> |
| |
| #include <vector> |
| #include <string> |
| |
| |
| using namespace armnn; |
| |
| class TestMemMgr : public IMemoryManager |
| { |
| public: |
| TestMemMgr() = default; |
| |
| void Acquire() override {} |
| void Release() override {} |
| }; |
| |
| class TestFactory1 : public ITensorHandleFactory |
| { |
| public: |
| TestFactory1(std::weak_ptr<IMemoryManager> mgr, ITensorHandleFactory::FactoryId id) |
| : m_Id(id) |
| , m_MemMgr(mgr) |
| {} |
| |
| std::unique_ptr<ITensorHandle> CreateSubTensorHandle(ITensorHandle& parent, |
| TensorShape const& subTensorShape, |
| unsigned int const* subTensorOrigin) const override |
| { |
| IgnoreUnused(parent, subTensorShape, subTensorOrigin); |
| return nullptr; |
| } |
| |
| std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo) const override |
| { |
| IgnoreUnused(tensorInfo); |
| return nullptr; |
| } |
| |
| std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo, |
| DataLayout dataLayout) const override |
| { |
| IgnoreUnused(tensorInfo, dataLayout); |
| return nullptr; |
| } |
| |
| const FactoryId& GetId() const override { return m_Id; } |
| |
| bool SupportsSubTensors() const override { return true; } |
| |
| MemorySourceFlags GetExportFlags() const override { return 1; } |
| |
| private: |
| FactoryId m_Id = "UninitializedId"; |
| |
| std::weak_ptr<IMemoryManager> m_MemMgr; |
| }; |
| |
| class TestFactoryImport : public ITensorHandleFactory |
| { |
| public: |
| TestFactoryImport(std::weak_ptr<IMemoryManager> mgr, ITensorHandleFactory::FactoryId id) |
| : m_Id(id) |
| , m_MemMgr(mgr) |
| {} |
| |
| std::unique_ptr<ITensorHandle> CreateSubTensorHandle(ITensorHandle& parent, |
| TensorShape const& subTensorShape, |
| unsigned int const* subTensorOrigin) const override |
| { |
| IgnoreUnused(parent, subTensorShape, subTensorOrigin); |
| return nullptr; |
| } |
| |
| std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo) const override |
| { |
| IgnoreUnused(tensorInfo); |
| return nullptr; |
| } |
| |
| std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo, |
| DataLayout dataLayout) const override |
| { |
| IgnoreUnused(tensorInfo, dataLayout); |
| return nullptr; |
| } |
| |
| const FactoryId& GetId() const override { return m_Id; } |
| |
| bool SupportsSubTensors() const override { return true; } |
| |
| MemorySourceFlags GetImportFlags() const override { return 1; } |
| |
| private: |
| FactoryId m_Id = "ImporterId"; |
| |
| std::weak_ptr<IMemoryManager> m_MemMgr; |
| }; |
| |
| class TestBackendA : public IBackendInternal |
| { |
| public: |
| TestBackendA() = default; |
| |
| const BackendId& GetId() const override { return m_Id; } |
| |
| IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override |
| { |
| IgnoreUnused(memoryManager); |
| return IWorkloadFactoryPtr{}; |
| } |
| |
| IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override |
| { |
| return ILayerSupportSharedPtr{}; |
| } |
| |
| std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override |
| { |
| return std::vector<ITensorHandleFactory::FactoryId> |
| { |
| "TestHandleFactoryA1", |
| "TestHandleFactoryA2", |
| "TestHandleFactoryB1", |
| "TestHandleFactoryD1" |
| }; |
| } |
| |
| void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override |
| { |
| auto mgr = std::make_shared<TestMemMgr>(); |
| |
| registry.RegisterMemoryManager(mgr); |
| registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryA1")); |
| registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryA2")); |
| } |
| |
| private: |
| BackendId m_Id = "BackendA"; |
| }; |
| |
| class TestBackendB : public IBackendInternal |
| { |
| public: |
| TestBackendB() = default; |
| |
| const BackendId& GetId() const override { return m_Id; } |
| |
| IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override |
| { |
| IgnoreUnused(memoryManager); |
| return IWorkloadFactoryPtr{}; |
| } |
| |
| IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override |
| { |
| return ILayerSupportSharedPtr{}; |
| } |
| |
| std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override |
| { |
| return std::vector<ITensorHandleFactory::FactoryId> |
| { |
| "TestHandleFactoryB1" |
| }; |
| } |
| |
| void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override |
| { |
| auto mgr = std::make_shared<TestMemMgr>(); |
| |
| registry.RegisterMemoryManager(mgr); |
| registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryB1")); |
| } |
| |
| private: |
| BackendId m_Id = "BackendB"; |
| }; |
| |
| class TestBackendC : public IBackendInternal |
| { |
| public: |
| TestBackendC() = default; |
| |
| const BackendId& GetId() const override { return m_Id; } |
| |
| IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override |
| { |
| IgnoreUnused(memoryManager); |
| return IWorkloadFactoryPtr{}; |
| } |
| |
| IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override |
| { |
| return ILayerSupportSharedPtr{}; |
| } |
| |
| std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override |
| { |
| return std::vector<ITensorHandleFactory::FactoryId>{ |
| "TestHandleFactoryC1" |
| }; |
| } |
| |
| void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override |
| { |
| auto mgr = std::make_shared<TestMemMgr>(); |
| |
| registry.RegisterMemoryManager(mgr); |
| registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryC1")); |
| } |
| |
| private: |
| BackendId m_Id = "BackendC"; |
| }; |
| |
| class TestBackendD : public IBackendInternal |
| { |
| public: |
| TestBackendD() = default; |
| |
| const BackendId& GetId() const override { return m_Id; } |
| |
| IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override |
| { |
| IgnoreUnused(memoryManager); |
| return IWorkloadFactoryPtr{}; |
| } |
| |
| IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override |
| { |
| return ILayerSupportSharedPtr{}; |
| } |
| |
| std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override |
| { |
| return std::vector<ITensorHandleFactory::FactoryId>{ |
| "TestHandleFactoryD1", |
| }; |
| } |
| |
| void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override |
| { |
| auto mgr = std::make_shared<TestMemMgr>(); |
| |
| registry.RegisterMemoryManager(mgr); |
| registry.RegisterFactory(std::make_unique<TestFactoryImport>(mgr, "TestHandleFactoryD1")); |
| } |
| |
| private: |
| BackendId m_Id = "BackendD"; |
| }; |
| |
| |
| TEST_SUITE("TensorHandle") |
| { |
| TEST_CASE("RegisterFactories") |
| { |
| TestBackendA backendA; |
| TestBackendB backendB; |
| |
| CHECK(backendA.GetHandleFactoryPreferences()[0] == "TestHandleFactoryA1"); |
| CHECK(backendA.GetHandleFactoryPreferences()[1] == "TestHandleFactoryA2"); |
| CHECK(backendA.GetHandleFactoryPreferences()[2] == "TestHandleFactoryB1"); |
| CHECK(backendA.GetHandleFactoryPreferences()[3] == "TestHandleFactoryD1"); |
| |
| TensorHandleFactoryRegistry registry; |
| backendA.RegisterTensorHandleFactories(registry); |
| backendB.RegisterTensorHandleFactories(registry); |
| |
| CHECK((registry.GetFactory("Non-existing Backend") == nullptr)); |
| CHECK((registry.GetFactory("TestHandleFactoryA1") != nullptr)); |
| CHECK((registry.GetFactory("TestHandleFactoryA2") != nullptr)); |
| CHECK((registry.GetFactory("TestHandleFactoryB1") != nullptr)); |
| } |
| |
| TEST_CASE("TensorHandleSelectionStrategy") |
| { |
| auto backendA = std::make_unique<TestBackendA>(); |
| auto backendB = std::make_unique<TestBackendB>(); |
| auto backendC = std::make_unique<TestBackendC>(); |
| auto backendD = std::make_unique<TestBackendD>(); |
| |
| TensorHandleFactoryRegistry registry; |
| backendA->RegisterTensorHandleFactories(registry); |
| backendB->RegisterTensorHandleFactories(registry); |
| backendC->RegisterTensorHandleFactories(registry); |
| backendD->RegisterTensorHandleFactories(registry); |
| |
| BackendsMap backends; |
| backends["BackendA"] = std::move(backendA); |
| backends["BackendB"] = std::move(backendB); |
| backends["BackendC"] = std::move(backendC); |
| backends["BackendD"] = std::move(backendD); |
| |
| armnn::Graph graph; |
| |
| armnn::InputLayer* const inputLayer = graph.AddLayer<armnn::InputLayer>(0, "input"); |
| inputLayer->SetBackendId("BackendA"); |
| |
| armnn::SoftmaxDescriptor smDesc; |
| armnn::SoftmaxLayer* const softmaxLayer1 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax1"); |
| softmaxLayer1->SetBackendId("BackendA"); |
| |
| armnn::SoftmaxLayer* const softmaxLayer2 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax2"); |
| softmaxLayer2->SetBackendId("BackendB"); |
| |
| armnn::SoftmaxLayer* const softmaxLayer3 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax3"); |
| softmaxLayer3->SetBackendId("BackendC"); |
| |
| armnn::SoftmaxLayer* const softmaxLayer4 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax4"); |
| softmaxLayer4->SetBackendId("BackendD"); |
| |
| armnn::OutputLayer* const outputLayer = graph.AddLayer<armnn::OutputLayer>(0, "output"); |
| outputLayer->SetBackendId("BackendA"); |
| |
| inputLayer->GetOutputSlot(0).Connect(softmaxLayer1->GetInputSlot(0)); |
| softmaxLayer1->GetOutputSlot(0).Connect(softmaxLayer2->GetInputSlot(0)); |
| softmaxLayer2->GetOutputSlot(0).Connect(softmaxLayer3->GetInputSlot(0)); |
| softmaxLayer3->GetOutputSlot(0).Connect(softmaxLayer4->GetInputSlot(0)); |
| softmaxLayer4->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); |
| |
| graph.TopologicalSort(); |
| |
| std::vector<std::string> errors; |
| auto result = SelectTensorHandleStrategy(graph, backends, registry, true, true, errors); |
| |
| CHECK(result.m_Error == false); |
| CHECK(result.m_Warning == false); |
| |
| OutputSlot& inputLayerOut = inputLayer->GetOutputSlot(0); |
| OutputSlot& softmaxLayer1Out = softmaxLayer1->GetOutputSlot(0); |
| OutputSlot& softmaxLayer2Out = softmaxLayer2->GetOutputSlot(0); |
| OutputSlot& softmaxLayer3Out = softmaxLayer3->GetOutputSlot(0); |
| OutputSlot& softmaxLayer4Out = softmaxLayer4->GetOutputSlot(0); |
| |
| // Check that the correct factory was selected |
| CHECK(inputLayerOut.GetTensorHandleFactoryId() == "TestHandleFactoryD1"); |
| CHECK(softmaxLayer1Out.GetTensorHandleFactoryId() == "TestHandleFactoryB1"); |
| CHECK(softmaxLayer2Out.GetTensorHandleFactoryId() == "TestHandleFactoryB1"); |
| CHECK(softmaxLayer3Out.GetTensorHandleFactoryId() == "TestHandleFactoryC1"); |
| CHECK(softmaxLayer4Out.GetTensorHandleFactoryId() == "TestHandleFactoryD1"); |
| |
| // Check that the correct strategy was selected |
| CHECK((inputLayerOut.GetEdgeStrategyForConnection(0) == EdgeStrategy::DirectCompatibility)); |
| CHECK((softmaxLayer1Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::DirectCompatibility)); |
| CHECK((softmaxLayer2Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::CopyToTarget)); |
| CHECK((softmaxLayer3Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::ExportToTarget)); |
| CHECK((softmaxLayer4Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::DirectCompatibility)); |
| |
| graph.AddCompatibilityLayers(backends, registry); |
| |
| // Test for copy layers |
| int copyCount= 0; |
| graph.ForEachLayer([©Count](Layer* layer) |
| { |
| if (layer->GetType() == LayerType::MemCopy) |
| { |
| copyCount++; |
| } |
| }); |
| CHECK(copyCount == 1); |
| |
| // Test for import layers |
| int importCount= 0; |
| graph.ForEachLayer([&importCount](Layer *layer) |
| { |
| if (layer->GetType() == LayerType::MemImport) |
| { |
| importCount++; |
| } |
| }); |
| CHECK(importCount == 1); |
| } |
| |
| TEST_CASE("RegisterCopyAndImportFactoryPairTest") |
| { |
| TensorHandleFactoryRegistry registry; |
| ITensorHandleFactory::FactoryId copyId = "CopyFactoryId"; |
| ITensorHandleFactory::FactoryId importId = "ImportFactoryId"; |
| registry.RegisterCopyAndImportFactoryPair(copyId, importId); |
| |
| // Get mathing import factory id correctly |
| CHECK((registry.GetMatchingImportFactoryId(copyId) == importId)); |
| |
| // Return empty id when Invalid Id is given |
| CHECK((registry.GetMatchingImportFactoryId("InvalidFactoryId") == "")); |
| } |
| |
| } |