ArmNN
 22.08
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 1690 of file LoadedNetwork.cpp.

Referenced by RuntimeImpl::ClearImportedInputs().

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

◆ ClearImportedOutputs()

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

Definition at line 1711 of file LoadedNetwork.cpp.

Referenced by RuntimeImpl::ClearImportedOutputs().

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

◆ 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 1954 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().

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

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

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

◆ FreeWorkingMemory()

void FreeWorkingMemory ( )

Definition at line 1229 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().

1230 {
1231 #if !defined(ARMNN_DISABLE_THREADS)
1232  std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
1233 #endif
1234 
1235  if (!m_IsWorkingMemAllocated)
1236  {
1237  return;
1238  }
1239 
1240  if (m_ExternalMemoryManager)
1241  {
1242  m_ExternalMemoryManager->Deallocate();
1243  }
1244 
1245  // Informs the memory managers to release memory in its respective memory group
1246  for (auto&& memoryManager : m_BackendMemoryMangers)
1247  {
1248  if (memoryManager)
1249  {
1250  memoryManager->Release();
1251  }
1252  }
1253  m_TensorHandleFactoryRegistry.ReleaseMemory();
1254  m_IsWorkingMemAllocated = false;
1255 }
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(), armnn::IgnoreUnused(), armnn::info, and IWorkloadFactory::IsLayerSupported().

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 1423 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().

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

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