ArmNN
 22.11
LoadedNetwork Class Reference

#include <LoadedNetwork.hpp>

Public Types

using WorkloadQueue = std::vector< std::unique_ptr< IWorkload > >
 

Public Member Functions

 ~LoadedNetwork ()
 
std::unique_ptr< IWorkingMemHandleCreateWorkingMemHandle (NetworkId networkId)
 Create a new unique WorkingMemHandle object. More...
 
TensorInfo GetInputTensorInfo (LayerBindingId layerId) const
 
TensorInfo GetOutputTensorInfo (LayerBindingId layerId) const
 
std::vector< ImportedInputIdImportInputs (const InputTensors &inputTensors, MemorySource forceImportMemorySource=MemorySource::Undefined)
 
std::vector< ImportedOutputIdImportOutputs (const OutputTensors &outputTensors, MemorySource forceImportMemorySource=MemorySource::Undefined)
 
void ClearImportedInputs (const std::vector< ImportedInputId > inputIds)
 
void ClearImportedOutputs (const std::vector< ImportedOutputId > outputIds)
 
Status EnqueueWorkload (const InputTensors &inputTensors, const OutputTensors &outputTensors, std::vector< ImportedInputId > preImportedInputIds={}, std::vector< ImportedOutputId > preImportedOutputIds={})
 Single thread execution of the loaded network. More...
 
Status Execute (const InputTensors &inputTensors, const OutputTensors &outputTensors, IWorkingMemHandle &workingMemHandle, std::vector< ImportedInputId > preImportedInputs={}, std::vector< ImportedOutputId > preImportedOutputs={})
 Thread safe execution of the loaded network. More...
 
const std::shared_ptr< IProfiler > & GetProfiler () const
 
void FreeWorkingMemory ()
 
void RegisterDebugCallback (const DebugCallbackFunction &func)
 
void SendNetworkStructure (arm::pipe::IProfilingService &profilingService)
 
bool IsAsyncEnabled ()
 
arm::pipe::ProfilingGuid GetNetworkGuid ()
 

Static Public Member Functions

static std::unique_ptr< LoadedNetworkMakeLoadedNetwork (std::unique_ptr< IOptimizedNetwork > net, std::string &errorMessage, const INetworkProperties &networkProperties, arm::pipe::IProfilingService *profilingService)
 

Detailed Description

Definition at line 42 of file LoadedNetwork.hpp.

Member Typedef Documentation

◆ WorkloadQueue

using WorkloadQueue = std::vector<std::unique_ptr<IWorkload> >

Definition at line 45 of file LoadedNetwork.hpp.

Constructor & Destructor Documentation

◆ ~LoadedNetwork()

~LoadedNetwork ( )
inline

Definition at line 47 of file LoadedNetwork.hpp.

48  {
50  }

Member Function Documentation

◆ ClearImportedInputs()

void ClearImportedInputs ( const std::vector< ImportedInputId inputIds)

Definition at line 1683 of file LoadedNetwork.cpp.

Referenced by RuntimeImpl::ClearImportedInputs().

1684 {
1685  for (auto id : inputIds)
1686  {
1687  if (id > m_PreImportedInputHandles.size())
1688  {
1689  throw InvalidArgumentException(fmt::format("ClearImportedInputs::Unknown ImportedInputId: {}", id));
1690  }
1691 
1692  auto& importedTensorHandle = m_PreImportedInputHandles[id].m_TensorHandle;
1693  if (!importedTensorHandle)
1694  {
1695  throw InvalidArgumentException(
1696  fmt::format("ClearImportedInputs::ImportedInput with id: {} has already been deleted", id));
1697  }
1698  // Call Unimport then destroy the tensorHandle
1699  importedTensorHandle->Unimport();
1700  importedTensorHandle = {};
1701  }
1702 }

◆ ClearImportedOutputs()

void ClearImportedOutputs ( const std::vector< ImportedOutputId outputIds)

Definition at line 1704 of file LoadedNetwork.cpp.

Referenced by RuntimeImpl::ClearImportedOutputs().

1705 {
1706  for (auto id : outputIds)
1707  {
1708  if (id > m_PreImportedOutputHandles.size())
1709  {
1710  throw InvalidArgumentException(fmt::format("ClearImportedOutputs::Unknown ImportedOutputId: {}", id));
1711  }
1712 
1713  auto& importedTensorHandle = m_PreImportedOutputHandles[id].m_TensorHandle;
1714  if (!importedTensorHandle)
1715  {
1716  throw InvalidArgumentException(
1717  fmt::format("ClearImportedOutputs::ImportedOutput with id: {} has already been deleted", id));
1718  }
1719  // Call Unimport then destroy the tensorHandle
1720  importedTensorHandle->Unimport();
1721  importedTensorHandle = {};
1722  }
1723 }

◆ CreateWorkingMemHandle()

std::unique_ptr< IWorkingMemHandle > CreateWorkingMemHandle ( NetworkId  networkId)

Create a new unique WorkingMemHandle object.

Create multiple handles if you wish to have overlapped Execution by calling this function from different threads.

Definition at line 1947 of file LoadedNetwork.cpp.

References ARMNN_ASSERT, ARMNN_NO_DEPRECATE_WARN_BEGIN, ARMNN_NO_DEPRECATE_WARN_END, ITensorHandleFactory::CreateTensorHandle(), Layer::GetBackendId(), and ITensorHandleFactory::LegacyFactoryId.

Referenced by RuntimeImpl::CreateWorkingMemHandle().

1948 {
1949  Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
1950 
1951  // Tensors that will need to be allocated internally within armnn
1952  std::vector<std::unique_ptr<ITensorHandle>> managedTensorHandles;
1953  // Tensors that will be allocated externally by the user
1954  std::vector<std::unique_ptr<ITensorHandle>> unmanagedTensorHandles;
1955 
1956  std::vector<WorkingMemDescriptor> workingMemDescriptors;
1957  std::vector<std::pair<BackendId, ExecutionData>> executionDataVec;
1958 
1959  auto GetTensorHandle = [&](Layer* layer, const OutputSlot& outputSlot)
1960  {
1961  ITensorHandleFactory::FactoryId factoryId = outputSlot.GetTensorHandleFactoryId();
1962  const TensorInfo& tensorInfo = outputSlot.GetTensorInfo();
1963 
1964  if (factoryId == ITensorHandleFactory::LegacyFactoryId)
1965  {
1966  BackendId id = layer->GetBackendId();
1968  return m_WorkloadFactories.at(id)->CreateTensorHandle(tensorInfo, false);
1970  }
1971  else
1972  {
1973  ITensorHandleFactory* handleFactory = m_TensorHandleFactoryRegistry.GetFactory(factoryId);
1974  ARMNN_ASSERT(handleFactory);
1975  return handleFactory->CreateTensorHandle(tensorInfo, false);
1976  }
1977  };
1978 
1979  struct HandleInfo
1980  {
1981  ITensorHandle* m_TensorHandle;
1982 
1983  bool m_IsInputLayerHandle = false;
1984  bool m_IsOutputLayerHandle = false;
1985 
1986  WorkingMemHandle::InputMemDescriptorCoords m_InputMemDescriptorCoords;
1987  WorkingMemHandle::OutputMemDescriptorCoords m_OutputMemDescriptorCoords;
1988  };
1989 
1990  std::unordered_map<const OutputSlot*, HandleInfo> outputToHandleInfoMap;
1991 
1992  unsigned int layerIndex = 0;
1993  for (auto&& layer : order)
1994  {
1995  // Constant layers execution and management is handled during loaded network construction
1996  if (layer->GetType() == LayerType::Constant)
1997  {
1998  continue;
1999  }
2000 
2001  WorkingMemDescriptor workingMemDescriptor;
2002 
2003  bool isMemoryManaged = true;
2004  bool isInputLayer = false;
2005  bool isOutputLayer = false;
2006  bool isConnectedToOutputLayer = false;
2007 
2008  if (layer->GetType() == LayerType::Input || layer->GetType() == LayerType::MemImport)
2009  {
2010  // Input layers/workloads will not be executed so the descriptor is not added to workingMemDescriptors
2011  // However we will still need to manage the tensorHandle
2012  isInputLayer = true;
2013  isMemoryManaged = !m_NetworkProperties.m_ImportEnabled;
2014  }
2015  else if (layer->GetType() == LayerType::Output)
2016  {
2017  isOutputLayer = true;
2018  }
2019 
2020  unsigned int slotIndex = 0;
2021  // Create a tensor handle for each output slot of a layer
2022  // Once we create it, we start managing its lifetime
2023  for (auto& slot : layer->GetOutputSlots())
2024  {
2025  for (unsigned int i = 0; i < slot.GetNumConnections(); ++i)
2026  {
2027  if ((slot.GetConnection(i)->GetOwningLayer().GetType() == LayerType::Output))
2028  {
2029  if (!isConnectedToOutputLayer)
2030  {
2031  isConnectedToOutputLayer = true;
2032  // If Export is enabled disable memory management, so we can export, otherwise we do a copy
2033  isMemoryManaged = !m_NetworkProperties.m_ExportEnabled;
2034  }
2035  else
2036  {
2037  // Importing in this case would likely cause unexpected behaviour, so we disallow it.
2038  ARMNN_LOG(warning) <<
2039  fmt::format("Layer name: '{0}' guid: '{1}' has two or more OutputLayers connected to it. "
2040  "This will prevent importing on the connected OutputLayers.",
2041  layer->GetName(), layer->GetGuid());
2042  isMemoryManaged = true;
2043  }
2044  }
2045  }
2046 
2047  ITensorHandle* tensorHandle;
2048  if (isMemoryManaged)
2049  {
2050  managedTensorHandles.emplace_back(GetTensorHandle(layer, slot));
2051  tensorHandle = managedTensorHandles.back().get();
2052  }
2053  else
2054  {
2055  unmanagedTensorHandles.emplace_back(GetTensorHandle(layer, slot));
2056  tensorHandle = unmanagedTensorHandles.back().get();
2057  }
2058 
2059  workingMemDescriptor.m_Outputs.push_back(tensorHandle);
2060 
2061  HandleInfo& handleInfo = outputToHandleInfoMap[&slot];
2062  handleInfo.m_TensorHandle = tensorHandle;
2063 
2064  // Store the coordinates of the current layer's OutputSlot that is connected to the OutputLayer
2065  if (isConnectedToOutputLayer)
2066  {
2067  handleInfo.m_IsOutputLayerHandle = true;
2068  handleInfo.m_OutputMemDescriptorCoords.m_OutputSlotCoords = {layerIndex, slotIndex};
2069  }
2070  // Store the LayerBindingId of the InputLayer
2071  if (isInputLayer)
2072  {
2073  handleInfo.m_IsInputLayerHandle = true;
2074  LayerBindingId bindingId = static_cast<BindableLayer*>(layer)->GetBindingId();
2075  handleInfo.m_InputMemDescriptorCoords.m_LayerBindingId = bindingId;
2076  }
2077  slotIndex++;
2078  }
2079  // Loop through the input slots in the same layer and decrement the reference counter associated
2080  // to each tensor handle we encounter.
2081  // Once it reaches zero, the lifetime of the tensor handle has ended, and we mark its memory as available
2082  // so that the next tensor handle with a non overlapping lifetime can share its memory.
2083  for (auto& slot : layer->GetInputSlots())
2084  {
2085  ARMNN_ASSERT(slot.GetConnection());
2086  auto outputSlot = slot.GetConnectedOutputSlot();
2087  auto key = outputSlot->GetOwningLayer().GetGuid();
2088 
2089  // Constant layers execution and management is handled during loaded network construction
2090  auto found = m_ConstantTensorHandles.find(key);
2091  if (found != m_ConstantTensorHandles.end())
2092  {
2093  ITensorHandle* tensorHandle = found->second;
2094  workingMemDescriptor.m_Inputs.push_back(tensorHandle);
2095 
2096  // Odd case where a constant layer is connected to an output layer
2097  // We will need to create a HandleInfo to track it
2098  if (isOutputLayer)
2099  {
2100  LayerBindingId bindingId = static_cast<BindableLayer*>(layer)->GetBindingId();
2101 
2102  HandleInfo& handleInfo = outputToHandleInfoMap[outputSlot];
2103  handleInfo.m_TensorHandle = tensorHandle;
2104  handleInfo.m_IsOutputLayerHandle = true;
2105  handleInfo.m_OutputMemDescriptorCoords.m_LayerBindingIds.push_back(bindingId);
2106  handleInfo.m_OutputMemDescriptorCoords.m_InputSlotCoords.push_back({layerIndex, 0});
2107  }
2108  continue;
2109  }
2110 
2111  HandleInfo& handleInfo = outputToHandleInfoMap.at(outputSlot);
2112 
2113  ITensorHandle* inputTensorHandle = handleInfo.m_TensorHandle;
2114  workingMemDescriptor.m_Inputs.push_back(inputTensorHandle);
2115 
2116  // Store the LayerBindingId of the OutputLayer
2117  if (isOutputLayer)
2118  {
2119  LayerBindingId bindingId = static_cast<BindableLayer*>(layer)->GetBindingId();
2120  handleInfo.m_OutputMemDescriptorCoords.m_LayerBindingIds.push_back(bindingId);
2121  handleInfo.m_OutputMemDescriptorCoords.m_InputSlotCoords.push_back({layerIndex, 0});
2122  }
2123  // In this case the layer is not an Output Layer but shares its input tensorhandle with an OutputLayer
2124  // It will need to be updated as well, if we swap out the tensorhandle
2125  else if (handleInfo.m_IsOutputLayerHandle)
2126  {
2127  handleInfo.m_OutputMemDescriptorCoords.m_InputSlotCoords.push_back({layerIndex, slot.GetSlotIndex()});
2128  }
2129 
2130  // Store the coordinates of the InputSlots connected to the InputLayer
2131  // There can be more than one InputSlot connected to an InputLayer, so we use a vector
2132  if (handleInfo.m_IsInputLayerHandle)
2133  {
2134  std::pair<LayerGuid, unsigned int> connectionLocation{layerIndex, slot.GetSlotIndex()};
2135  handleInfo.m_InputMemDescriptorCoords.m_InputSlotCoords.emplace_back(connectionLocation);
2136  }
2137  }
2138 
2139  // Input/Output layers/workloads will not be executed, so the descriptor is not added to workingMemDescriptors
2140  // However we will still need to manage the tensorHandle
2141  if (!isInputLayer)
2142  {
2143  // Simply auto initialise ExecutionData here, so it's added only for the layer that require execution.
2144  // The memory and data will be allocated/assigned for the void* in WorkingMemHandle::Allocate.
2145  std::pair<BackendId, ExecutionData> dataPair;
2146  dataPair.first = layer->GetBackendId();
2147 
2148  executionDataVec.push_back(dataPair);
2149  workingMemDescriptors.push_back(workingMemDescriptor);
2150 
2151  layerIndex++;
2152  }
2153  }
2154 
2155  std::vector<std::pair<std::shared_ptr<TensorMemory>, MemorySource>> tensorMemory;
2156 
2157  auto externalMemoryManager = CreateExternalMemoryManger(tensorMemory);
2158 
2159  // Sort m_TensorMemory, so it's order matches the outputSlot order
2160  std::sort(tensorMemory.begin(), tensorMemory.end(),
2161  [](const std::pair<std::shared_ptr<TensorMemory>, MemorySource>& lhs,
2162  const std::pair<std::shared_ptr<TensorMemory>, MemorySource>& rhs)
2163  {
2164  return lhs.first->m_OutputSlotId < rhs.first->m_OutputSlotId;
2165  });
2166 
2167  std::vector<WorkingMemHandle::InputMemDescriptorCoords> inputConnectionsInfo;
2168  std::vector<WorkingMemHandle::OutputMemDescriptorCoords> outputConnectionsInfo;
2169 
2170  for (const auto& handleInfo: outputToHandleInfoMap)
2171  {
2172  if (handleInfo.second.m_IsOutputLayerHandle)
2173  {
2174  outputConnectionsInfo.emplace_back(handleInfo.second.m_OutputMemDescriptorCoords);
2175  }
2176 
2177  if (handleInfo.second.m_IsInputLayerHandle)
2178  {
2179  inputConnectionsInfo.emplace_back(handleInfo.second.m_InputMemDescriptorCoords);
2180  }
2181  }
2182 
2183  return std::make_unique<WorkingMemHandle>(networkId,
2184  inputConnectionsInfo,
2185  outputConnectionsInfo,
2186  workingMemDescriptors,
2187  std::move(externalMemoryManager),
2188  std::move(tensorMemory),
2189  std::move(managedTensorHandles),
2190  std::move(unmanagedTensorHandles),
2191  executionDataVec,
2192  &m_Backends);
2193 }
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
Definition: Deprecated.hpp:33
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:290
#define ARMNN_NO_DEPRECATE_WARN_END
Definition: Deprecated.hpp:34
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
MemorySource
Define the Memory Source to reduce copies.
Definition: Types.hpp:230
ITensorHandleFactory * GetFactory(ITensorHandleFactory::FactoryId id) const
Find a TensorHandleFactory by Id Returns nullptr if not found.
static const FactoryId LegacyFactoryId

◆ EnqueueWorkload()

Status EnqueueWorkload ( const InputTensors inputTensors,
const OutputTensors outputTensors,
std::vector< ImportedInputId preImportedInputIds = {},
std::vector< ImportedOutputId preImportedOutputIds = {} 
)

Single thread execution of the loaded network.

Definition at line 839 of file LoadedNetwork.cpp.

References ARMNN_ASSERT_MSG, ARMNN_LOG, ARMNN_SCOPED_PROFILING_EVENT, armnn::Failure, OutputHandler::GetData(), Graph::GetInputLayers(), Graph::GetNumInputs(), Graph::GetNumLayers(), Graph::GetNumOutputs(), Graph::GetOutputLayers(), armnn::info, QueueDescriptor::m_Inputs, WorkloadInfo::m_InputTensorInfos, armnn::Undefined, and armnn::warning.

Referenced by RuntimeImpl::EnqueueWorkload().

843 {
844  const Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
845 
846  // Walk graph to determine the order of execution.
847  if (graph.GetNumLayers() < 2)
848  {
849  ARMNN_LOG(warning) << "IRuntime::EnqueueWorkload()::Less than two nodes in graph";
850  return Status::Failure;
851  }
852 
853  // Data that must be kept alive for the entire execution of the workload.
854  WorkloadData workloadData(inputTensors, outputTensors);
855 
856  // Input tensors can be provided as parameters or pre imported. Either way the number of
857  // tensors should match the number of inputs.
858  if (graph.GetNumInputs() != (inputTensors.size() + preImportedInputIds.size()))
859  {
860  throw InvalidArgumentException("Number of inputs provided does not match network.");
861  }
862 
863  // For each input to the network, call EnqueueInput with the data passed by the user.
864  {
866  m_InputQueue.clear();
867  m_InputQueue.reserve(graph.GetNumInputs());
868 
869  unsigned int inputIndex = 0;
870  unsigned int importedInputIdIndex = 0;
871  std::sort(preImportedInputIds.begin(), preImportedInputIds.end());
872  for (const BindableLayer* inputLayer : graph.GetInputLayers())
873  {
874  if (importedInputIdIndex < preImportedInputIds.size() &&
875  inputIndex == preImportedInputIds[importedInputIdIndex])
876  {
877  // Only replace tensorhandles if they have not already been replaced
878  if (!m_IsInputImported[inputIndex])
879  {
880  auto outputTensorHandle = m_PreImportedInputHandles[inputIndex].m_TensorHandle.get();
881 
882  for (const auto& workloadInfo: m_InputWorkloadSlotPairs[inputLayer->GetBindingId()])
883  {
884  auto workload = m_WorkloadQueue[workloadInfo.m_WorkloadIndex].get();
885  workload->ReplaceInputTensorHandle(outputTensorHandle, workloadInfo.m_SlotIndex);
886  }
887  m_IsInputImported[inputIndex] = true;
888  }
889  importedInputIdIndex++;
890  }
891  else
892  {
893  if (m_IsInputImported[inputIndex])
894  {
895  OutputHandler& handler = const_cast<OutputHandler&>(inputLayer->GetOutputHandler(0));
896 
897  for (const auto& workloadInfo: m_InputWorkloadSlotPairs[inputLayer->GetBindingId()])
898  {
899  auto workload = m_WorkloadQueue[workloadInfo.m_WorkloadIndex].get();
900  workload->ReplaceInputTensorHandle(handler.GetData(), workloadInfo.m_SlotIndex);
901  }
902 
903  m_IsInputImported[inputIndex] = false;
904  }
905 
906  // InputTensorHandle is not imported yet, process to enqueue input
907  const TensorPin& pin = workloadData.GetInputTensorPin(inputLayer->GetBindingId());
908  EnqueueInput(*inputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
909  }
910  inputIndex++;
911  }
912  }
913  // For each output to the network, call EnqueueOutput with the data passed by the user.
914  {
916  m_OutputQueue.clear();
917  m_OutputQueue.reserve(graph.GetNumOutputs());
918 
919  if (preImportedOutputIds.size() > graph.GetNumOutputs())
920  {
921  throw InvalidArgumentException("Invalid number of preImportedOutputIds");
922  }
923 
924  unsigned int outputIndex = 0;
925  unsigned int importedOutputIdIndex = 0;
926  std::sort(preImportedOutputIds.begin(), preImportedOutputIds.end());
927  for (const BindableLayer* outputLayer : graph.GetOutputLayers())
928  {
929  if (importedOutputIdIndex < preImportedOutputIds.size() &&
930  outputIndex == preImportedOutputIds[importedOutputIdIndex])
931  {
932  // Only replace tensorhandles if they have not already been replaced
933  ITensorHandle* inputTensorHandle = m_PreImportedOutputHandles[outputIndex].m_TensorHandle.get();
934 
935  if (!m_IsOutputImported[outputIndex])
936  {
937  const auto bindingId = outputLayer->GetBindingId();
938  const auto& indices = m_OutputWorkloadSlotPairs[bindingId];
939 
940  auto outputWorkload = m_WorkloadQueue[indices.m_OutputSlotIndices.m_WorkloadIndex].get();
941 
942  outputWorkload->ReplaceOutputTensorHandle(inputTensorHandle,
943  indices.m_OutputSlotIndices.m_SlotIndex);
944 
945  for (const auto& workloadInfo: indices.m_InputSlotIndices)
946  {
947  auto inputWorkload = m_WorkloadQueue[workloadInfo.m_WorkloadIndex].get();
948  inputWorkload->ReplaceInputTensorHandle(inputTensorHandle, workloadInfo.m_SlotIndex);
949  }
950  m_IsOutputImported[outputIndex] = true;
951  }
952 
953  ARMNN_ASSERT_MSG(inputTensorHandle != nullptr, "Data should have been allocated.");
954  MemSyncQueueDescriptor syncDesc;
955  syncDesc.m_Inputs.push_back(inputTensorHandle);
956  WorkloadInfo info;
957  info.m_InputTensorInfos.push_back(
958  outputLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo());
959  auto syncWorkload = std::make_unique<SyncMemGenericWorkload>(syncDesc, info);
960  ARMNN_ASSERT_MSG(syncWorkload, "No sync workload created");
961  m_OutputQueue.push_back(move(syncWorkload));
962  importedOutputIdIndex++;
963  }
964  else
965  {
966  if (m_IsOutputImported[outputIndex])
967  {
968  const auto bindingId = outputLayer->GetBindingId();
969  const auto& indices = m_OutputWorkloadSlotPairs[bindingId];
970 
971  auto outputWorkload = m_WorkloadQueue[indices.m_OutputSlotIndices.m_WorkloadIndex].get();
972  const OutputHandler& outputHandler =
973  outputLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetOutputHandler();
974 
975  outputWorkload->ReplaceOutputTensorHandle(
976  outputHandler.GetData(), indices.m_OutputSlotIndices.m_SlotIndex);
977 
978  for (const auto& workloadInfo: indices.m_InputSlotIndices)
979  {
980  auto inputWorkload = m_WorkloadQueue[workloadInfo.m_WorkloadIndex].get();
981  inputWorkload->ReplaceInputTensorHandle(outputHandler.GetData(), workloadInfo.m_SlotIndex);
982  }
983  m_IsOutputImported[outputIndex] = false;
984  }
985 
986  const TensorPin& pin = workloadData.GetOutputTensorPin(outputLayer->GetBindingId());
987  // OutputTensorHandle is not imported yet, process to enqueue Output
988  EnqueueOutput(*outputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
989  }
990  outputIndex++;
991  }
992  }
993 
994  std::unique_ptr<TimelineUtilityMethods> timelineUtils =
995  TimelineUtilityMethods::GetTimelineUtils(*m_ProfilingService);
996  ProfilingGuid inferenceGuid = m_ProfilingService->GetNextGuid();
997  if (timelineUtils)
998  {
999  // Add inference timeline trace if profiling is enabled.
1000  ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
1001  timelineUtils->CreateTypedEntity(inferenceGuid, LabelsAndEventClasses::INFERENCE_GUID);
1002  timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
1003  networkGuid,
1004  inferenceGuid,
1005  LabelsAndEventClasses::EXECUTION_OF_GUID);
1006  timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
1007  }
1008 
1009  bool executionSucceeded = true;
1010 
1011  {
1012  if (m_ProfilingService->IsProfilingEnabled())
1013  {
1014  m_ProfilingService->IncrementCounterValue(INFERENCES_RUN);
1015  }
1017  ARMNN_SCOPED_HEAP_PROFILING("Executing");
1018  executionSucceeded = Execute(timelineUtils, inferenceGuid);
1019  }
1020 
1021  if (timelineUtils)
1022  {
1023  // Add end of life of the inference timeline if profiling is enabled.
1024  timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
1025  timelineUtils->Commit();
1026  }
1027 
1028  return executionSucceeded ? Status::Success : Status::Failure;
1029 }
Status Execute(const InputTensors &inputTensors, const OutputTensors &outputTensors, IWorkingMemHandle &workingMemHandle, std::vector< ImportedInputId > preImportedInputs={}, std::vector< ImportedOutputId > preImportedOutputs={})
Thread safe execution of the loaded network.
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
Definition: Profiling.hpp:220
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
#define ARMNN_SCOPED_HEAP_PROFILING(TAG)

◆ Execute()

Status Execute ( const InputTensors inputTensors,
const OutputTensors outputTensors,
IWorkingMemHandle workingMemHandle,
std::vector< ImportedInputId preImportedInputs = {},
std::vector< ImportedOutputId preImportedOutputs = {} 
)

Thread safe execution of the loaded network.

Definition at line 1725 of file LoadedNetwork.cpp.

References WorkingMemHandle::Allocate(), ARMNN_LOG, ARMNN_SCOPED_PROFILING_EVENT, armnn::CopyToOutputTensor(), armnn::error, armnn::Failure, WorkingMemHandle::GetBindingIdVector(), WorkingMemHandle::GetExecutionDataAt(), WorkingMemHandle::GetInputConnections(), WorkingMemHandle::GetInputHandle(), Graph::GetNumInputs(), Graph::GetNumOutputs(), WorkingMemHandle::GetOutputConnection(), WorkingMemHandle::GetOutputHandle(), WorkingMemHandle::IsAllocated(), WorkingMemHandle::MemSyncOutputs(), armnn::Success, armnn::Undefined, and WorkingMemHandle::ValidateBindingIds().

Referenced by RuntimeImpl::Execute(), and LoadedNetwork::FreeWorkingMemory().

1730 {
1731  const Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
1732 
1733  if (inputTensors.size() + preImportedInputs.size() != graph.GetNumInputs())
1734  {
1735  if (preImportedInputs.empty())
1736  {
1737  throw InvalidArgumentException("LoadedNetwork::Execute: Number of inputs provided does not match network.");
1738  }
1739  else
1740  {
1741  throw InvalidArgumentException("LoadedNetwork::Execute: "
1742  "Number of inputs + preImportedInputs provided does not match network.");
1743  }
1744  }
1745 
1746  if (outputTensors.size() + preImportedOutputs.size() != graph.GetNumOutputs())
1747  {
1748  if (preImportedOutputs.empty())
1749  {
1750  throw InvalidArgumentException("LoadedNetwork::Execute: "
1751  "Number of outputs provided does not match network.");
1752  }
1753  else
1754  {
1755  throw InvalidArgumentException("LoadedNetwork::Execute: "
1756  "Number of outputs + preImportedOutputs provided does not match network.");
1757  }
1758  }
1759 
1760  WorkingMemHandle& workingMemHandle = dynamic_cast<WorkingMemHandle&>(iWorkingMemHandle);
1761  // Collect all the given LayerBindingIds and check them for duplicates and unknowns.
1762  std::vector<LayerBindingId>& bindingIds = workingMemHandle.GetBindingIdVector();
1763  unsigned int index = 0;
1764  for (auto pair : inputTensors)
1765  {
1766  bindingIds[index++] = pair.first;
1767  }
1768  for (ImportedInputId id : preImportedInputs)
1769  {
1770  bindingIds[index++] = ValidateImportedInputID(id);
1771  }
1772  for (auto pair : outputTensors)
1773  {
1774  bindingIds[index++] = pair.first;
1775  }
1776  for (ImportedOutputId id : preImportedOutputs)
1777  {
1778  bindingIds[index++] = ValidateImportedOutputID(id);
1779  }
1780 
1781  workingMemHandle.ValidateBindingIds();
1782 
1783  auto resetMemHandle = [&]()
1784  {
1785  for (ImportedInputId id: preImportedInputs)
1786  {
1787  const LayerBindingId layerBindingId = m_PreImportedInputHandles[id].m_LayerBindingId;
1788 
1789  auto inputHandle = workingMemHandle.GetInputHandle(layerBindingId);
1790  auto inputConnections = workingMemHandle.GetInputConnections(layerBindingId);
1791  for (auto it : inputConnections)
1792  {
1793  *it = inputHandle;
1794  }
1795  }
1796 
1797  for (ImportedOutputId id: preImportedOutputs)
1798  {
1799  const LayerBindingId layerBindingId = m_PreImportedOutputHandles[id].m_LayerBindingId;
1800 
1801  auto outputHandle = workingMemHandle.GetOutputHandle(layerBindingId);
1802  auto outputConnections = workingMemHandle.GetOutputConnection(layerBindingId);
1803 
1804  for (auto it : outputConnections)
1805  {
1806  *it = outputHandle;
1807  }
1808  }
1809  };
1810 
1811  std::unique_ptr<TimelineUtilityMethods> timelineUtils =
1812  TimelineUtilityMethods::GetTimelineUtils(*m_ProfilingService);
1813  ProfilingGuid inferenceGuid = m_ProfilingService->GetNextGuid();
1814  if (timelineUtils)
1815  {
1816  // Add inference timeline trace if profiling is enabled.
1817  ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
1818  timelineUtils->CreateTypedEntity(inferenceGuid,LabelsAndEventClasses::INFERENCE_GUID);
1819  timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
1820  networkGuid,
1821  inferenceGuid,
1822  LabelsAndEventClasses::EXECUTION_OF_GUID);
1823  timelineUtils->RecordEvent(inferenceGuid,LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
1824  }
1825 
1826  bool executionSucceeded = true;
1827 
1828  if (timelineUtils)
1829  {
1830  // Add end of life of the inference timeline if profiling is enabled.
1831  timelineUtils->RecordEvent(inferenceGuid,LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
1832  timelineUtils->Commit();
1833  }
1834 
1835  if (!workingMemHandle.IsAllocated())
1836  {
1837  workingMemHandle.Allocate();
1838  }
1839 
1840  {
1842  for (auto pair : inputTensors)
1843  {
1844  EnqueueInput(pair.second, workingMemHandle.GetInputHandle(pair.first));
1845  }
1846 
1847  // Swap in the pre-imported inputs if any
1848  for (ImportedInputId id : preImportedInputs)
1849  {
1850  const ImportedTensorHandlePin& importedInputPin = m_PreImportedInputHandles[id];
1851  const LayerBindingId layerBindingId = m_PreImportedInputHandles[id].m_LayerBindingId;
1852  const auto& preimportedHandle = importedInputPin.m_TensorHandle;
1853 
1854  auto inputConnections = workingMemHandle.GetInputConnections(layerBindingId);
1855  for (auto it : inputConnections)
1856  {
1857  *it = preimportedHandle.get();
1858  }
1859  }
1860  }
1861  {
1863  if (m_NetworkProperties.m_ExportEnabled)
1864  {
1865  for (auto pair: outputTensors)
1866  {
1867  ImportOutputTensor(pair.second, workingMemHandle.GetOutputHandle(pair.first));
1868  }
1869  }
1870 
1871  for (ImportedOutputId id : preImportedOutputs)
1872  {
1873  const ImportedTensorHandlePin& importedOutputPin = m_PreImportedOutputHandles[id];
1874  const LayerBindingId layerBindingId = m_PreImportedOutputHandles[id].m_LayerBindingId;
1875  const auto& preimportedHandle = importedOutputPin.m_TensorHandle;
1876 
1877  auto outputConnections = workingMemHandle.GetOutputConnection(layerBindingId);
1878  for (auto it : outputConnections)
1879  {
1880  *it = preimportedHandle.get();
1881  }
1882  }
1883  }
1884 
1885  auto Fail = [&](const std::exception& error)
1886  {
1887  ARMNN_LOG(error) << "An error occurred attempting to execute a workload: " << error.what();
1888  executionSucceeded = false;
1889  };
1890  ProfilingDynamicGuid workloadInferenceID(0);
1891 
1892  try
1893  {
1894  for (unsigned int i = 0; i < m_WorkloadQueue.size(); ++i)
1895  {
1896  auto& workload = m_WorkloadQueue[i];
1897  if (timelineUtils)
1898  {
1899  workloadInferenceID = timelineUtils->RecordWorkloadInferenceAndStartOfLifeEvent(workload->GetGuid(),
1900  inferenceGuid);
1901  }
1902 
1903  workload->ExecuteAsync(workingMemHandle.GetExecutionDataAt(i).second);
1904 
1905  if (timelineUtils)
1906  {
1907  timelineUtils->RecordEndOfLifeEvent(workloadInferenceID);
1908  }
1909  }
1910  }
1911  catch (const RuntimeException& error)
1912  {
1913  resetMemHandle();
1914  Fail(error);
1915  }
1916  catch (const std::runtime_error& error)
1917  {
1918  resetMemHandle();
1919  Fail(error);
1920  }
1921  catch (...)
1922  {
1923  resetMemHandle();
1924  throw;
1925  }
1926 
1927  if (!m_NetworkProperties.m_ExportEnabled)
1928  {
1929  for (auto pair: outputTensors)
1930  {
1931  CopyToOutputTensor(pair.second, workingMemHandle.GetOutputHandle(pair.first));
1932  }
1933  }
1934  else
1935  {
1936  ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "SyncMemGeneric_Execute");
1937  workingMemHandle.MemSyncOutputs();
1938  }
1939 
1940  resetMemHandle();
1941 
1942  return executionSucceeded ? Status::Success : Status::Failure;
1943 }
unsigned int ImportedOutputId
Definition: Types.hpp:292
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
void CopyToOutputTensor(const Tensor &outputTensor, ITensorHandle *outputTensorHandle)
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
Definition: Profiling.hpp:220
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:290
const bool m_ExportEnabled
Deprecated and will be removed in future release.
Definition: IRuntime.hpp:64
unsigned int ImportedInputId
Definition: Types.hpp:291

◆ FreeWorkingMemory()

void FreeWorkingMemory ( )

Definition at line 1222 of file LoadedNetwork.cpp.

References ARMNN_ASSERT_MSG, ARMNN_LOG, armnn::CheckFlag(), armnn::CopyTensorContentsGeneric(), armnn::error, LoadedNetwork::Execute(), ITensorHandle::GetImportFlags(), BaseTensor< MemoryType >::GetInfo(), BaseTensor< MemoryType >::GetMemoryArea(), ITensorHandle::Import(), and ITensorHandle::Map().

Referenced by RuntimeImpl::CreateWorkingMemHandle(), and RuntimeImpl::EnqueueWorkload().

1223 {
1224 #if !defined(ARMNN_DISABLE_THREADS)
1225  std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
1226 #endif
1227 
1228  if (!m_IsWorkingMemAllocated)
1229  {
1230  return;
1231  }
1232 
1233  if (m_ExternalMemoryManager)
1234  {
1235  m_ExternalMemoryManager->Deallocate();
1236  }
1237 
1238  // Informs the memory managers to release memory in its respective memory group
1239  for (auto&& memoryManager : m_BackendMemoryMangers)
1240  {
1241  if (memoryManager)
1242  {
1243  memoryManager->Release();
1244  }
1245  }
1246  m_TensorHandleFactoryRegistry.ReleaseMemory();
1247  m_IsWorkingMemAllocated = false;
1248 }
void ReleaseMemory()
Release memory required for inference.

◆ GetInputTensorInfo()

TensorInfo GetInputTensorInfo ( LayerBindingId  layerId) const

Definition at line 697 of file LoadedNetwork.cpp.

References ARMNN_ASSERT_MSG.

Referenced by RuntimeImpl::GetInputTensorInfo().

698 {
699  for (auto&& inputLayer : m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetInputLayers())
700  {
701  ARMNN_ASSERT_MSG(inputLayer->GetNumOutputSlots() == 1, "Input layer should have exactly 1 output slot");
702  if (inputLayer->GetBindingId() == layerId)
703  {
704  return inputLayer->GetOutputSlot(0).GetTensorInfo();
705  }
706  }
707 
708  throw InvalidArgumentException(fmt::format("No input layer is associated with id {}", layerId));
709 }
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15

◆ GetNetworkGuid()

ProfilingGuid GetNetworkGuid ( )

Definition at line 692 of file LoadedNetwork.cpp.

693 {
694  return m_OptimizedNetwork->GetGuid();
695 }

◆ GetOutputTensorInfo()

TensorInfo GetOutputTensorInfo ( LayerBindingId  layerId) const

Definition at line 711 of file LoadedNetwork.cpp.

References ARMNN_ASSERT_MSG, CHECK_LOCATION, BackendId::Get(), Layer::GetBackendId(), Layer::GetNameStr(), and armnn::info.

Referenced by RuntimeImpl::GetOutputTensorInfo().

712 {
713  for (auto&& outputLayer : m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetOutputLayers())
714  {
715  ARMNN_ASSERT_MSG(outputLayer->GetNumInputSlots() == 1, "Output layer should have exactly 1 input slot");
716  ARMNN_ASSERT_MSG(outputLayer->GetInputSlot(0).GetConnection(), "Input slot on Output layer must be connected");
717  if (outputLayer->GetBindingId() == layerId)
718  {
719  return outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo();
720  }
721  }
722 
723  throw InvalidArgumentException(fmt::format("No output layer is associated with id {}", layerId));
724 }
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15

◆ GetProfiler()

const std::shared_ptr<IProfiler>& GetProfiler ( ) const
inline

Definition at line 87 of file LoadedNetwork.hpp.

Referenced by RuntimeImpl::CreateWorkingMemHandle(), RuntimeImpl::EnqueueWorkload(), and RuntimeImpl::Execute().

87 { return m_OptimizedNetwork->GetProfiler(); }

◆ ImportInputs()

std::vector< ImportedInputId > ImportInputs ( const InputTensors inputTensors,
MemorySource  forceImportMemorySource = MemorySource::Undefined 
)

Definition at line 1416 of file LoadedNetwork.cpp.

References ARMNN_ASSERT, ARMNN_LOG, Graph::InputLayersAccessor::begin(), ITensorHandle::CanBeImported(), armnn::CheckFlag(), ITensorHandleFactory::CreateTensorHandle(), Graph::InputLayersAccessor::end(), Layer::GetBackendId(), BindableLayer::GetBindingId(), ITensorHandle::GetImportFlags(), BaseTensor< MemoryType >::GetInfo(), Graph::GetInputLayers(), BaseTensor< MemoryType >::GetMemoryArea(), Layer::GetOutputSlots(), OutputSlot::GetTensorHandleFactoryId(), OutputSlot::GetTensorInfo(), Layer::GetType(), armnn::HasCapability(), ITensorHandle::Import(), armnn::Input, Graph::TopologicalSort(), armnn::Undefined, and Exception::what().

Referenced by RuntimeImpl::ImportInputs().

1418 {
1419  if (!m_NetworkProperties.m_AsyncEnabled)
1420  {
1421  // Cannot import if import is not enabled and forceImportMemorySource is undefined
1422  if (forceImportMemorySource == MemorySource::Undefined)
1423  {
1424  throw MemoryImportException("ImportInputs: Memory Import failed, NetworkProperties.m_ImportEnabled");
1425  }
1426  // The number of pre imported tensors should not exceed the number of inputs.
1427  if (inputTensors.size() > m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetNumInputs())
1428  {
1429  throw MemoryImportException("ImportInputs: The number of tensors provided exceeds the number of inputs.");
1430  }
1431 
1432  std::vector<ImportedInputId> importedInputs;
1433  Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
1434  unsigned int inputIndex = 0;
1435  for (const BindableLayer* inputLayer : graph.GetInputLayers())
1436  {
1437  auto outputTensorHandle = m_PreImportedInputHandles[inputIndex].m_TensorHandle.get();
1438 
1439  if (!outputTensorHandle)
1440  {
1441  inputIndex++;
1442  continue;
1443  }
1444 
1445  auto layerBindingId = inputLayer->GetBindingId();
1446  auto it = std::find_if(inputTensors.begin(), inputTensors.end(), [=](const auto& inputTensor)
1447  {
1448  return inputTensor.first == layerBindingId;
1449  });
1450 
1451  if (it == inputTensors.end())
1452  {
1453  inputIndex++;
1454  continue;
1455  }
1456 
1457  const auto& inputTensor = *it;
1458  std::unique_ptr<ITensorHandle> passThroughTensorHandle =
1459  std::make_unique<ConstPassthroughTensorHandle>(inputTensor.second.GetInfo(),
1460  inputTensor.second.GetMemoryArea());
1461 
1462  try
1463  {
1464  if (outputTensorHandle->CanBeImported(passThroughTensorHandle->Map(), forceImportMemorySource)
1465  && (outputTensorHandle->Import(passThroughTensorHandle->Map(), forceImportMemorySource)))
1466  {
1467  importedInputs.push_back(inputIndex);
1468  }
1469  passThroughTensorHandle->Unmap();
1470  }
1471  catch(const MemoryImportException& exception)
1472  {
1473  ARMNN_LOG(error) << "An error occurred attempting to import input_"
1474  << inputIndex << " : " << exception.what();
1475  passThroughTensorHandle->Unmap();
1476  }
1477  inputIndex++;
1478  }
1479 
1480  return importedInputs;
1481  }
1482  else
1483  {
1484  // Import when the import of network properties is enabled
1485  std::vector<ImportedInputId> importedInputs;
1486  Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
1487 
1488  for (auto inputTensor : inputTensors)
1489  {
1490  auto layerBindingId = inputTensor.first;
1491  auto it = std::find_if(graph.GetInputLayers().begin(), graph.GetInputLayers().end(), [=](auto* layer)
1492  {
1493  return layer->GetBindingId() == layerBindingId;
1494  });
1495 
1496  if (it == graph.GetInputLayers().end())
1497  {
1498  throw MemoryImportException(fmt::format(
1499  "ImportInputs: Memory Import failed, unknown LayerBindingId: {}", layerBindingId));
1500  }
1501 
1502  const Layer* layer = *it;
1503  if (layer->GetType() != LayerType::Input)
1504  {
1505  throw InvalidArgumentException("ImportInputs: given layer not an InputLayer");
1506  }
1507 
1508  auto& backend = m_Backends.at(layer->GetBackendId());
1509  if (!HasCapability(BackendOptions::BackendOption{"PreImportIOTensors", true}, backend->GetCapabilities()))
1510  {
1511  std::string er = backend->GetId();
1512  er += " does not have PreImportIOTensors capability";
1513  throw BackendCapabilityException(er);
1514  }
1515 
1516  const OutputSlot& outputSlot = layer->GetOutputSlots()[0];
1517 
1518  ITensorHandleFactory::FactoryId factoryId = outputSlot.GetTensorHandleFactoryId();
1519  const TensorInfo& tensorInfo = outputSlot.GetTensorInfo();
1520 
1521  ITensorHandleFactory* handleFactory = m_TensorHandleFactoryRegistry.GetFactory(factoryId);
1522  ARMNN_ASSERT(handleFactory);
1523 
1524  ImportedTensorHandlePin importedTensorHandlePin{layerBindingId,
1525  handleFactory->CreateTensorHandle(tensorInfo, false)};
1526 
1527  ITensorHandle* tensorHandle = importedTensorHandlePin.m_TensorHandle.get();
1528 
1529  if (!CheckFlag(tensorHandle->GetImportFlags(), forceImportMemorySource))
1530  {
1531  throw MemoryImportException(
1532  fmt::format("ImportInputs: Memory Import failed, backend: "
1533  "{} does not support importing from source {}"
1534  , factoryId, m_NetworkProperties.m_InputSource));
1535  }
1536 
1537  std::unique_ptr<ITensorHandle> passThroughTensorHandle =
1538  std::make_unique<ConstPassthroughTensorHandle>(inputTensor.second.GetInfo(),
1539  inputTensor.second.GetMemoryArea());
1540 
1541  if (tensorHandle->Import(passThroughTensorHandle->Map(), forceImportMemorySource))
1542  {
1543  importedInputs.push_back(m_CurImportedInputId++);
1544  passThroughTensorHandle->Unmap();
1545  }
1546  else
1547  {
1548  passThroughTensorHandle->Unmap();
1549  throw MemoryImportException("ImportInputs: Memory Import failed");
1550  }
1551 
1552  m_PreImportedInputHandles.push_back(std::move(importedTensorHandlePin));
1553  }
1554  return importedInputs;
1555  }
1556 }
bool HasCapability(const std::string &name, const BackendCapabilities &capabilities)
Convenience function to check if a capability exists in a BackendCapabilites struct.
const MemorySource m_InputSource
Definition: IRuntime.hpp:72
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
ITensorHandleFactory * GetFactory(ITensorHandleFactory::FactoryId id) const
Find a TensorHandleFactory by Id Returns nullptr if not found.
bool CheckFlag(MemorySourceFlags flags, MemorySource source)

◆ ImportOutputs()

std::vector< ImportedOutputId > ImportOutputs ( const OutputTensors outputTensors,
MemorySource  forceImportMemorySource = MemorySource::Undefined 
)

Definition at line 1558 of file LoadedNetwork.cpp.

References ARMNN_ASSERT, ARMNN_LOG, Graph::OutputLayersAccessor::begin(), ITensorHandle::CanBeImported(), armnn::CheckFlag(), ITensorHandleFactory::CreateTensorHandle(), Graph::OutputLayersAccessor::end(), Layer::GetBackendId(), BindableLayer::GetBindingId(), InputSlot::GetConnectedOutputSlot(), ITensorHandle::GetImportFlags(), Layer::GetInputSlots(), BaseTensor< MemoryType >::GetMemoryArea(), Graph::GetOutputLayers(), OutputSlot::GetTensorHandleFactoryId(), OutputSlot::GetTensorInfo(), Layer::GetType(), armnn::HasCapability(), ITensorHandle::Import(), armnn::Output, Graph::TopologicalSort(), armnn::Undefined, and Exception::what().

Referenced by RuntimeImpl::ImportOutputs().

1560 {
1561  if (!m_NetworkProperties.m_AsyncEnabled)
1562  {
1563  // Cannot import if import is not enabled and forceImportMemorySource is undefined
1564  if (forceImportMemorySource == MemorySource::Undefined)
1565  {
1566  throw MemoryImportException("ImportOutputs: Memory Import failed, NetworkProperties.m_ImportEnabled");
1567  }
1568  // If forceImportMemorySource is defined, try import if memory is aligned
1569  if (outputTensors.size() != m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetNumOutputs())
1570  {
1571  throw MemoryImportException("ImportOutputs: Force Import failed, incorrect number of tensors");
1572  }
1573  std::vector<ImportedOutputId> importedOutputs;
1574  Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
1575 
1576  unsigned int outputIndex = 0;
1577  for (const BindableLayer* const outputLayer : graph.GetOutputLayers())
1578  {
1579  auto inputTensorHandle = m_PreImportedOutputHandles[outputIndex].m_TensorHandle.get();
1580  if (!inputTensorHandle)
1581  {
1582  outputIndex++;
1583  continue;
1584  }
1585 
1586  auto layerBindingId = outputLayer->GetBindingId();
1587  auto it = std::find_if(outputTensors.begin(), outputTensors.end(), [=] (const auto& outputTensor)
1588  {
1589  return outputTensor.first == layerBindingId;
1590  });
1591 
1592  if (it == outputTensors.end())
1593  {
1594  outputIndex++;
1595  continue;
1596  }
1597 
1598  const auto outputTensor = *it;
1599  try
1600  {
1601  // Check if the output memory can be imported
1602  if (inputTensorHandle->CanBeImported(outputTensor.second.GetMemoryArea(), forceImportMemorySource)
1603  && inputTensorHandle->Import(outputTensor.second.GetMemoryArea(), forceImportMemorySource))
1604  {
1605  importedOutputs.push_back(outputIndex);
1606  }
1607  }
1608  catch(const MemoryImportException& exception)
1609  {
1610  ARMNN_LOG(error) << "An error occurred attempting to import output_"
1611  << outputIndex << " : " << exception.what();
1612  }
1613  outputIndex++;
1614  }
1615  return importedOutputs;
1616  }
1617 
1618  std::vector<ImportedOutputId> importedOutputs;
1619  Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
1620 
1621  for (const auto& outputTensor : outputTensors)
1622  {
1623  auto layerBindingId = outputTensor.first;
1624  auto it = std::find_if(graph.GetOutputLayers().begin(), graph.GetOutputLayers().end(), [=](auto* layer)
1625  {
1626  return layer->GetBindingId() == layerBindingId;
1627  });
1628 
1629  if (it == graph.GetOutputLayers().end())
1630  {
1631  throw MemoryImportException(fmt::format("ImportOutputs: Memory Import failed, unknown LayerBindingId: {}",
1632  layerBindingId));
1633  }
1634 
1635  const Layer* layer = *it;
1636  if (layer->GetType() != LayerType::Output)
1637  {
1638  throw InvalidArgumentException("ImportOutputs: given layer not an OutputLayer");
1639  }
1640 
1641  auto& backend = m_Backends.at(layer->GetBackendId());
1642  if (!HasCapability(BackendOptions::BackendOption{"PreImportIOTensors", true}, backend->GetCapabilities()))
1643  {
1644  std::string er = backend->GetId();
1645  er += " does not have PreImportIOTensors capability";
1646  throw BackendCapabilityException(er);
1647  }
1648 
1649  const InputSlot& inputSlot = layer->GetInputSlots()[0];
1650  ITensorHandleFactory::FactoryId factoryId = inputSlot.GetConnectedOutputSlot()->GetTensorHandleFactoryId();
1651  const TensorInfo& tensorInfo = inputSlot.GetConnectedOutputSlot()->GetTensorInfo();
1652 
1653  ITensorHandleFactory* handleFactory = m_TensorHandleFactoryRegistry.GetFactory(factoryId);
1654  ARMNN_ASSERT(handleFactory);
1655 
1656  ImportedTensorHandlePin importedTensorHandlePin{layerBindingId,
1657  handleFactory->CreateTensorHandle(tensorInfo, false)};
1658 
1659  ITensorHandle* tensorHandle = importedTensorHandlePin.m_TensorHandle.get();
1660 
1661  if (!CheckFlag(tensorHandle->GetImportFlags(), forceImportMemorySource))
1662  {
1663  throw MemoryImportException(fmt::format("ImportInputs: Memory Import failed, backend: "
1664  "{} does not support importing from source {}"
1665  , factoryId, forceImportMemorySource));
1666  }
1667 
1668  if (tensorHandle->Import(outputTensor.second.GetMemoryArea(), forceImportMemorySource))
1669  {
1670  importedOutputs.push_back(m_CurImportedOutputId++);
1671  }
1672  else
1673  {
1674  throw MemoryImportException("ImportInputs: Memory Import failed");
1675  }
1676 
1677  m_PreImportedOutputHandles.push_back(std::move(importedTensorHandlePin));
1678  }
1679 
1680  return importedOutputs;
1681 }
bool HasCapability(const std::string &name, const BackendCapabilities &capabilities)
Convenience function to check if a capability exists in a BackendCapabilites struct.
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
ITensorHandleFactory * GetFactory(ITensorHandleFactory::FactoryId id) const
Find a TensorHandleFactory by Id Returns nullptr if not found.
bool CheckFlag(MemorySourceFlags flags, MemorySource source)

◆ IsAsyncEnabled()

bool IsAsyncEnabled ( )
inline

Definition at line 95 of file LoadedNetwork.hpp.

Referenced by RuntimeImpl::CreateWorkingMemHandle(), RuntimeImpl::EnqueueWorkload(), and RuntimeImpl::Execute().

96  {
97  return m_NetworkProperties.m_AsyncEnabled;
98  }

◆ MakeLoadedNetwork()

std::unique_ptr< LoadedNetwork > MakeLoadedNetwork ( std::unique_ptr< IOptimizedNetwork net,
std::string &  errorMessage,
const INetworkProperties networkProperties,
arm::pipe::IProfilingService *  profilingService 
)
static

Definition at line 170 of file LoadedNetwork.cpp.

References ITensorHandle::Allocate(), ARMNN_ASSERT, ARMNN_LOG, ARMNN_SCOPED_PROFILING_EVENT, armnn::BackendRegistryInstance(), armnn::Constant, IBackendInternal::CreateMemoryManager(), ITensorHandleFactory::CreateTensorHandle(), IBackendInternal::CreateWorkloadFactory(), armnn::error, IBackendInternal::GetCapabilities(), BackendRegistry::GetFactory(), IBackend::GetId(), ProfilerManager::GetInstance(), BackendRegistry::GetMemoryOptimizerStrategies(), Graph::GetNumInputs(), Graph::GetNumOutputs(), armnn::HasCapability(), armnn::Input, ITensorHandleFactory::LegacyFactoryId, INetworkProperties::m_AsyncEnabled, INetworkProperties::m_OutputNetworkDetailsMethod, WorkingMemDescriptor::m_Outputs, INetworkProperties::m_ProfilingEnabled, armnn::MemImport, armnn::numeric_cast(), armnn::Output, ProfilerManager::RegisterProfiler(), Graph::SetLayersOutOfOrder(), IBackendInternal::SupportsTensorAllocatorAPI(), Graph::TopologicalSort(), armnn::Undefined, and armnn::ValidateSourcesMatchOptimizedNetwork().

Referenced by RuntimeImpl::LoadNetwork().

174 {
175  std::unique_ptr<LoadedNetwork> loadedNetwork;
176 
177  auto Fail = [&](const std::exception& error) -> std::unique_ptr<LoadedNetwork>
178  {
179  errorMessage = ToErrorMessage("An error occurred when preparing the network workloads: ", error);
180  ARMNN_LOG(error) << errorMessage;
181 
182  return std::unique_ptr<LoadedNetwork>();
183  };
184 
185  try
186  {
187  loadedNetwork.reset(new LoadedNetwork(std::move(net), networkProperties, profilingService));
188  }
189  catch (const armnn::RuntimeException& error)
190  {
191  return Fail(error);
192  }
193  catch (const armnn::Exception& error)
194  {
195  return Fail(error);
196  }
197  catch (const std::runtime_error& error)
198  {
199  return Fail(error);
200  }
201 
202  return loadedNetwork;
203 }
#define ARMNN_LOG(severity)
Definition: Logging.hpp:205
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46

◆ RegisterDebugCallback()

◆ SendNetworkStructure()

void SendNetworkStructure ( arm::pipe::IProfilingService &  profilingService)

Definition at line 654 of file LoadedNetwork.cpp.

References ARMNN_SCOPED_PROFILING_EVENT, armnn::Input, armnn::Output, Graph::TopologicalSort(), and armnn::Undefined.

655 {
656  ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "LoadNetwork_SendNetworkStructure");
657  Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
658  ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
659 
660  std::unique_ptr<TimelineUtilityMethods> timelineUtils =
661  TimelineUtilityMethods::GetTimelineUtils(profilingService);
662 
663  timelineUtils->CreateTypedEntity(networkGuid, LabelsAndEventClasses::NETWORK_GUID);
664 
665  for (auto&& layer : order)
666  {
667  // Add layer to the post-optimisation network structure
668  AddLayerStructure(timelineUtils, *layer, networkGuid);
669  switch (layer->GetType())
670  {
671  case LayerType::Input:
672  case LayerType::Output:
673  {
674  // Inputs and outputs are treated in a special way - see EnqueueInput() and EnqueueOutput().
675  break;
676  }
677  default:
678  {
679  for (auto& workload : m_WorkloadQueue)
680  {
681  // Add workload to the post-optimisation network structure
682  AddWorkloadStructure(timelineUtils, workload, *layer);
683  }
684  break;
685  }
686  }
687  }
688  // Commit to send the post-optimisation network structure
689  timelineUtils->Commit();
690 }
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
Definition: Profiling.hpp:220

The documentation for this class was generated from the following files: