From bfeb2711da172b26931c58af7b15d434ef49e24e Mon Sep 17 00:00:00 2001 From: Ferran Balaguer Date: Wed, 7 Aug 2019 15:14:56 +0100 Subject: IVGCVSW-3606 Support memory import for Reference backend Signed-off-by: Ferran Balaguer Change-Id: I94bd191f88e0911ad4e4727610e81cd7afa95512 --- src/backends/reference/CMakeLists.txt | 2 + src/backends/reference/RefBackend.cpp | 24 ++++++++ src/backends/reference/RefBackend.hpp | 7 +++ src/backends/reference/RefTensorHandle.cpp | 55 ++++++++++++++++- src/backends/reference/RefTensorHandle.hpp | 12 ++++ src/backends/reference/RefTensorHandleFactory.cpp | 62 +++++++++++++++++++ src/backends/reference/RefTensorHandleFactory.hpp | 54 ++++++++++++++++ src/backends/reference/backend.mk | 1 + src/backends/reference/test/RefEndToEndTests.cpp | 72 ++++++++++++++++++++++ .../reference/test/RefTensorHandleTests.cpp | 52 ++++++++++++++++ 10 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 src/backends/reference/RefTensorHandleFactory.cpp create mode 100644 src/backends/reference/RefTensorHandleFactory.hpp (limited to 'src/backends/reference') diff --git a/src/backends/reference/CMakeLists.txt b/src/backends/reference/CMakeLists.txt index 963e64379d..6852ab0b74 100644 --- a/src/backends/reference/CMakeLists.txt +++ b/src/backends/reference/CMakeLists.txt @@ -16,6 +16,8 @@ list(APPEND armnnRefBackend_sources RefRegistryInitializer.cpp RefWorkloadFactory.cpp RefWorkloadFactory.hpp + RefTensorHandleFactory.hpp + RefTensorHandleFactory.cpp ) add_library(armnnRefBackend OBJECT ${armnnRefBackend_sources}) diff --git a/src/backends/reference/RefBackend.cpp b/src/backends/reference/RefBackend.cpp index 41438b0151..2b56416b31 100644 --- a/src/backends/reference/RefBackend.cpp +++ b/src/backends/reference/RefBackend.cpp @@ -7,6 +7,7 @@ #include "RefBackendId.hpp" #include "RefWorkloadFactory.hpp" #include "RefLayerSupport.hpp" +#include "RefTensorHandleFactory.hpp" #include #include @@ -32,6 +33,16 @@ IBackendInternal::IWorkloadFactoryPtr RefBackend::CreateWorkloadFactory( return std::make_unique(boost::polymorphic_pointer_downcast(memoryManager)); } +IBackendInternal::IWorkloadFactoryPtr RefBackend::CreateWorkloadFactory( + class TensorHandleFactoryRegistry& tensorHandleFactoryRegistry) const +{ + auto memoryManager = std::make_shared(); + + tensorHandleFactoryRegistry.RegisterMemoryManager(memoryManager); + + return std::make_unique(boost::polymorphic_pointer_downcast(memoryManager)); +} + IBackendInternal::IBackendContextPtr RefBackend::CreateBackendContext(const IRuntime::CreationOptions&) const { return IBackendContextPtr{}; @@ -62,4 +73,17 @@ OptimizationViews RefBackend::OptimizeSubgraphView(const SubgraphView& subgraph) return optimizationViews; } +std::vector RefBackend::GetHandleFactoryPreferences() const +{ + return std::vector { RefTensorHandleFactory::GetIdStatic() }; +} + +void RefBackend::RegisterTensorHandleFactories(class TensorHandleFactoryRegistry& registry) +{ + auto memoryManager = std::make_shared(); + + registry.RegisterMemoryManager(memoryManager); + registry.RegisterFactory(std::make_unique(memoryManager)); +} + } // namespace armnn diff --git a/src/backends/reference/RefBackend.hpp b/src/backends/reference/RefBackend.hpp index 06179bd939..86e8b4c7bb 100644 --- a/src/backends/reference/RefBackend.hpp +++ b/src/backends/reference/RefBackend.hpp @@ -23,12 +23,19 @@ public: IBackendInternal::IWorkloadFactoryPtr CreateWorkloadFactory( const IBackendInternal::IMemoryManagerSharedPtr& memoryManager = nullptr) const override; + IBackendInternal::IWorkloadFactoryPtr CreateWorkloadFactory( + class TensorHandleFactoryRegistry& tensorHandleFactoryRegistry) const override; + IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override; IBackendInternal::Optimizations GetOptimizations() const override; IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override; OptimizationViews OptimizeSubgraphView(const SubgraphView& subgraph) const override; + + std::vector GetHandleFactoryPreferences() const override; + + void RegisterTensorHandleFactories(class TensorHandleFactoryRegistry& registry) override; }; } // namespace armnn diff --git a/src/backends/reference/RefTensorHandle.cpp b/src/backends/reference/RefTensorHandle.cpp index fe9310f423..59ccec6bac 100644 --- a/src/backends/reference/RefTensorHandle.cpp +++ b/src/backends/reference/RefTensorHandle.cpp @@ -11,7 +11,21 @@ RefTensorHandle::RefTensorHandle(const TensorInfo &tensorInfo, std::shared_ptr(MemorySource::Undefined)), + m_Imported(false) +{ + +} + +RefTensorHandle::RefTensorHandle(const TensorInfo& tensorInfo, std::shared_ptr &memoryManager, + MemorySourceFlags importFlags) + : m_TensorInfo(tensorInfo), + m_MemoryManager(memoryManager), + m_Pool(nullptr), + m_UnmanagedMemory(nullptr), + m_ImportFlags(importFlags), + m_Imported(false) { } @@ -86,4 +100,43 @@ void RefTensorHandle::CopyInFrom(const void* src) memcpy(dest, src, m_TensorInfo.GetNumBytes()); } +bool RefTensorHandle::Import(void* memory, MemorySource source) +{ + + if (m_ImportFlags & static_cast(source)) + { + if (source == MemorySource::Malloc) + { + // Checks the 16 byte memory alignment. + if (reinterpret_cast(memory) % 16) + { + return false; + } + + // m_UnmanagedMemory not yet allocated. + if (!m_Imported && !m_UnmanagedMemory) + { + m_UnmanagedMemory = memory; + m_Imported = true; + return true; + } + + // m_UnmanagedMemory initially allocated with Allocate(). + if (!m_Imported && m_UnmanagedMemory) + { + return false; + } + + // m_UnmanagedMemory previously imported. + if (m_Imported) + { + m_UnmanagedMemory = memory; + return true; + } + } + } + + return false; +} + } diff --git a/src/backends/reference/RefTensorHandle.hpp b/src/backends/reference/RefTensorHandle.hpp index ad47ee597f..6cde3263a0 100644 --- a/src/backends/reference/RefTensorHandle.hpp +++ b/src/backends/reference/RefTensorHandle.hpp @@ -17,6 +17,9 @@ class RefTensorHandle : public ITensorHandle public: RefTensorHandle(const TensorInfo& tensorInfo, std::shared_ptr &memoryManager); + RefTensorHandle(const TensorInfo& tensorInfo, std::shared_ptr &memoryManager, + MemorySourceFlags importFlags); + ~RefTensorHandle(); virtual void Manage() override; @@ -49,6 +52,13 @@ public: return m_TensorInfo; } + virtual MemorySourceFlags GetImportFlags() const override + { + return m_ImportFlags; + } + + virtual bool Import(void* memory, MemorySource source) override; + private: // Only used for testing void CopyOutTo(void*) const override; @@ -64,6 +74,8 @@ private: std::shared_ptr m_MemoryManager; RefMemoryManager::Pool* m_Pool; mutable void *m_UnmanagedMemory; + MemorySourceFlags m_ImportFlags; + bool m_Imported; }; } diff --git a/src/backends/reference/RefTensorHandleFactory.cpp b/src/backends/reference/RefTensorHandleFactory.cpp new file mode 100644 index 0000000000..c97a779cb3 --- /dev/null +++ b/src/backends/reference/RefTensorHandleFactory.cpp @@ -0,0 +1,62 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "RefTensorHandleFactory.hpp" +#include "RefTensorHandle.hpp" + +#include + +namespace armnn +{ + +using FactoryId = ITensorHandleFactory::FactoryId; + +const FactoryId& RefTensorHandleFactory::GetIdStatic() +{ + static const FactoryId s_Id(RefTensorHandleFactoryId()); + return s_Id; +} + +std::unique_ptr RefTensorHandleFactory::CreateSubTensorHandle(ITensorHandle& parent, + TensorShape const& subTensorShape, + unsigned int const* subTensorOrigin) const +{ + boost::ignore_unused(parent, subTensorShape, subTensorOrigin); + return nullptr; +} + +std::unique_ptr RefTensorHandleFactory::CreateTensorHandle(const TensorInfo& tensorInfo) const +{ + return std::make_unique(tensorInfo, m_MemoryManager, m_ImportFlags); +} + +std::unique_ptr RefTensorHandleFactory::CreateTensorHandle(const TensorInfo& tensorInfo, + DataLayout dataLayout) const +{ + boost::ignore_unused(dataLayout); + return std::make_unique(tensorInfo, m_MemoryManager, m_ImportFlags); +} + +const FactoryId& RefTensorHandleFactory::GetId() const +{ + return GetIdStatic(); +} + +bool RefTensorHandleFactory::SupportsSubTensors() const +{ + return false; +} + +MemorySourceFlags RefTensorHandleFactory::GetExportFlags() const +{ + return m_ExportFlags; +} + +MemorySourceFlags RefTensorHandleFactory::GetImportFlags() const +{ + return m_ImportFlags; +} + +} // namespace armnn \ No newline at end of file diff --git a/src/backends/reference/RefTensorHandleFactory.hpp b/src/backends/reference/RefTensorHandleFactory.hpp new file mode 100644 index 0000000000..220e6fd0de --- /dev/null +++ b/src/backends/reference/RefTensorHandleFactory.hpp @@ -0,0 +1,54 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "RefMemoryManager.hpp" + +#include + +namespace armnn +{ + +constexpr const char * RefTensorHandleFactoryId() { return "Arm/Ref/TensorHandleFactory"; } + +class RefTensorHandleFactory : public ITensorHandleFactory +{ + +public: + RefTensorHandleFactory(std::shared_ptr mgr) + : m_MemoryManager(mgr), + m_ImportFlags(static_cast(MemorySource::Malloc)), + m_ExportFlags(static_cast(MemorySource::Malloc)) + {} + + std::unique_ptr CreateSubTensorHandle(ITensorHandle& parent, + TensorShape const& subTensorShape, + unsigned int const* subTensorOrigin) const override; + + std::unique_ptr CreateTensorHandle(const TensorInfo& tensorInfo) const override; + + std::unique_ptr CreateTensorHandle(const TensorInfo& tensorInfo, + DataLayout dataLayout) const override; + + static const FactoryId& GetIdStatic(); + + const FactoryId& GetId() const override; + + bool SupportsSubTensors() const override; + + MemorySourceFlags GetExportFlags() const override; + + MemorySourceFlags GetImportFlags() const override; + +private: + mutable std::shared_ptr m_MemoryManager; + MemorySourceFlags m_ImportFlags; + MemorySourceFlags m_ExportFlags; + +}; + +} // namespace armnn + diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk index b212995ad1..a8df565947 100644 --- a/src/backends/reference/backend.mk +++ b/src/backends/reference/backend.mk @@ -14,6 +14,7 @@ BACKEND_SOURCES := \ RefTensorHandle.cpp \ RefWorkloadFactory.cpp \ RefRegistryInitializer.cpp \ + RefTensorHandleFactory.cpp \ workloads/Activation.cpp \ workloads/BatchNormImpl.cpp \ workloads/BatchToSpaceNd.cpp \ diff --git a/src/backends/reference/test/RefEndToEndTests.cpp b/src/backends/reference/test/RefEndToEndTests.cpp index f81f1a708f..31e9b339ec 100644 --- a/src/backends/reference/test/RefEndToEndTests.cpp +++ b/src/backends/reference/test/RefEndToEndTests.cpp @@ -322,6 +322,78 @@ BOOST_AUTO_TEST_CASE(TrivialMin) BOOST_TEST(outputData[3] == 2); } +BOOST_AUTO_TEST_CASE(RefNoCopyWorkloads) +{ + using namespace armnn; + + // Create runtime in which test will run + IRuntime::CreationOptions options; + IRuntimePtr runtime(armnn::IRuntime::Create(options)); + + // build up the structure of the network + INetworkPtr net(INetwork::Create()); + + IConnectableLayer* input = net->AddInputLayer(0); + + NormalizationDescriptor descriptor; + IConnectableLayer* norm = net->AddNormalizationLayer(descriptor); + + IConnectableLayer* output = net->AddOutputLayer(0); + + input->GetOutputSlot(0).Connect(norm->GetInputSlot(0)); + norm->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32)); + norm->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32)); + + // Optimize the network + IOptimizedNetworkPtr optNet = Optimize(*net, defaultBackends, runtime->GetDeviceSpec()); + + // Loads it into the runtime. + NetworkId netId; + runtime->LoadNetwork(netId, std::move(optNet)); + + // Creates structures for input & output + std::vector inputData + { + 1.0f, 2.0f, 3.0f, 4.0f + }; + + std::vector outputData(4); + + InputTensors inputTensors + { + {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())}, + }; + OutputTensors outputTensors + { + {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())} + }; + + // The result of the inference is not important, just the fact that there + // should not be CopyMemGeneric workloads. + runtime->GetProfiler(netId)->EnableProfiling(true); + + // Do the inference + runtime->EnqueueWorkload(netId, inputTensors, outputTensors); + + // Retrieve the Profiler.Print() output to get the workload execution + ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance(); + std::stringstream ss; + profilerManager.GetProfiler()->Print(ss);; + std::string dump = ss.str(); + + // Contains RefNormalizationWorkload + std::size_t found = dump.find("RefNormalizationWorkload"); + BOOST_TEST(found != std::string::npos); + // Contains SyncMemGeneric + found = dump.find("SyncMemGeneric"); + BOOST_TEST(found != std::string::npos); + // No contains CopyMemGeneric + found = dump.find("CopyMemGeneric"); + BOOST_TEST(found == std::string::npos); +} + BOOST_AUTO_TEST_CASE(RefEqualSimpleEndToEndTest) { const std::vector expectedOutput({ 1, 1, 1, 1, 0, 0, 0, 0, diff --git a/src/backends/reference/test/RefTensorHandleTests.cpp b/src/backends/reference/test/RefTensorHandleTests.cpp index accf900975..19b669b42f 100644 --- a/src/backends/reference/test/RefTensorHandleTests.cpp +++ b/src/backends/reference/test/RefTensorHandleTests.cpp @@ -45,4 +45,56 @@ BOOST_AUTO_TEST_CASE(AcquireAndRelease) memoryManager->Release(); } +BOOST_AUTO_TEST_CASE(CheckSourceType) +{ + std::shared_ptr memoryManager = std::make_shared(); + + TensorInfo info({1}, DataType::Float32); + RefTensorHandle handle(info, memoryManager, static_cast(MemorySource::Malloc)); + + // This pointer will be deleted in the handle destructor + int* testPtr = new int(4); + + // Not supported + BOOST_CHECK(!handle.Import(static_cast(testPtr), MemorySource::DmaBuf)); + + // Not supported + BOOST_CHECK(!handle.Import(static_cast(testPtr), MemorySource::DmaBufProtected)); + + // Supported + BOOST_CHECK(handle.Import(static_cast(testPtr), MemorySource::Malloc)); +} + +BOOST_AUTO_TEST_CASE(ReusePointer) +{ + std::shared_ptr memoryManager = std::make_shared(); + + TensorInfo info({1}, DataType::Float32); + RefTensorHandle handle(info, memoryManager, static_cast(MemorySource::Malloc)); + + // This pointer will be deleted in the handle destructor + int* testPtr = new int(4); + + handle.Import(static_cast(testPtr), MemorySource::Malloc); + + // Reusing previously Imported pointer + BOOST_CHECK(handle.Import(static_cast(testPtr), MemorySource::Malloc)); +} + +BOOST_AUTO_TEST_CASE(MisalignedPointer) +{ + std::shared_ptr memoryManager = std::make_shared(); + + TensorInfo info({2}, DataType::Float32); + RefTensorHandle handle(info, memoryManager, static_cast(MemorySource::Malloc)); + + // Allocates a 2 int array + int* testPtr = new int[2]; + int* misalignedPtr = testPtr + 1; + + BOOST_CHECK(!handle.Import(static_cast(misalignedPtr), MemorySource::Malloc)); + + delete[] testPtr; +} + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file -- cgit v1.2.1