2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include "LoadedNetwork.hpp"
10 #include <Processes.hpp>
11 #include "Profiling.hpp"
12 #include "HeapProfiling.hpp"
14 #include <armnn/BackendRegistry.hpp>
15 #include <armnn/Logging.hpp>
16 #include <armnn/utility/Assert.hpp>
18 #include <backendsCommon/CpuTensorHandle.hpp>
19 #include <armnn/backends/IMemoryManager.hpp>
20 #include <backendsCommon/MemCopyWorkload.hpp>
21 #include <backendsCommon/MemSyncWorkload.hpp>
23 #include <LabelsAndEventClasses.hpp>
25 #include <fmt/format.h>
31 using namespace armnn::profiling;
36 template <typename ExceptionType>
37 std::string ToErrorMessage(const char * prefix, const ExceptionType & error)
40 ss << prefix << " " << error.what();
44 void AddLayerStructure(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
46 ProfilingGuid networkGuid)
48 // Add layer to the post-optimisation network structure
49 std::string layerName = layer.GetNameStr().empty() ? "<Unnamed>" : layer.GetNameStr();
50 timelineUtils->CreateNamedTypedChildEntity(layer.GetGuid(),
53 LabelsAndEventClasses::LAYER_GUID);
54 for (auto&& input : layer.GetInputSlots())
56 const IOutputSlot* source = input.GetConnectedOutputSlot();
57 ARMNN_ASSERT(source != NULL);
58 timelineUtils->CreateConnectionRelationship(ProfilingRelationshipType::RetentionLink,
59 source->GetOwningLayerGuid(),
64 void AddWorkloadStructure(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
65 std::unique_ptr<IWorkload>& workload,
68 // Add workload to the post-optimisation network structure
69 timelineUtils->CreateTypedEntity(workload->GetGuid(), LabelsAndEventClasses::WORKLOAD_GUID);
70 timelineUtils->MarkEntityWithLabel(workload->GetGuid(),
71 layer.GetBackendId().Get(),
72 LabelsAndEventClasses::BACKENDID_GUID);
74 // Link the workload to the layer
75 timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
78 LabelsAndEventClasses::CHILD_GUID);
83 std::unique_ptr<LoadedNetwork> LoadedNetwork::MakeLoadedNetwork(std::unique_ptr<OptimizedNetwork> net,
84 std::string& errorMessage,
85 const INetworkProperties& networkProperties,
86 profiling::ProfilingService& profilingService)
88 std::unique_ptr<LoadedNetwork> loadedNetwork;
90 auto Fail = [&](const std::exception& error) -> std::unique_ptr<LoadedNetwork>
92 errorMessage = ToErrorMessage("An error occurred when preparing the network workloads: ", error);
93 ARMNN_LOG(error) << errorMessage;
95 return std::unique_ptr<LoadedNetwork>();
100 loadedNetwork.reset(new LoadedNetwork(std::move(net), networkProperties, profilingService));
102 catch (const armnn::RuntimeException& error)
106 catch (const armnn::Exception& error)
110 catch (const std::runtime_error& error)
115 return loadedNetwork;
118 LoadedNetwork::LoadedNetwork(std::unique_ptr<OptimizedNetwork> net,
119 const INetworkProperties& networkProperties,
120 profiling::ProfilingService& profilingService) :
121 m_OptimizedNetwork(std::move(net)),
122 m_IsImportEnabled(networkProperties.m_ImportEnabled),
123 m_IsExportEnabled(networkProperties.m_ExportEnabled),
124 m_TensorHandleFactoryRegistry(),
125 m_ProfilingService(profilingService)
127 // Create a profiler and register it for the current thread.
128 m_Profiler = std::make_shared<Profiler>();
129 ProfilerManager::GetInstance().RegisterProfiler(m_Profiler.get());
131 Graph& order = m_OptimizedNetwork->GetGraph().TopologicalSort();
132 //First create tensor handlers, backends and workload factories.
133 //Handlers are created before workloads are.
134 //Because workload creation can modify some of the handlers,
135 //(for example the splitter and concat layers).
136 for (auto&& layer : order)
138 auto const& backendId = layer->GetBackendId();
139 if (m_Backends.count(backendId) == 0)
141 auto createBackend = BackendRegistryInstance().GetFactory(backendId);
142 auto it = m_Backends.emplace(std::make_pair(backendId, createBackend()));
144 IBackendInternal* backend = it.first->second.get();
146 if (backend->SupportsTensorAllocatorAPI())
148 auto workloadFactory = backend->CreateWorkloadFactory(
149 m_TensorHandleFactoryRegistry, m_OptimizedNetwork->GetModelOptions());
150 m_WorkloadFactories.emplace(
151 std::make_pair(backendId, std::make_pair(std::move(workloadFactory), nullptr)));
155 IBackendInternal::IMemoryManagerSharedPtr memoryManager = backend->CreateMemoryManager();
156 auto workloadFactory = backend->CreateWorkloadFactory(
157 memoryManager, m_OptimizedNetwork->GetModelOptions());
159 m_WorkloadFactories.emplace(
160 std::make_pair(backendId, std::make_pair(std::move(workloadFactory), memoryManager)));
165 for (auto&& layer : order)
167 auto& workloadFactory = GetWorkloadFactory(*layer);
169 switch (layer->GetType())
171 case LayerType::Input:
172 case LayerType::MemImport:
174 // If IsImportEnabled is true then we need to set IsMemoryManaged to false when creating TensorHandles
175 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry, workloadFactory, !m_IsImportEnabled);
180 // Look for the layer with 1 OutputSlot which has 1 connection and that connection is an Output Layer
181 // If Export is enabled disable memory management so we can export, otherwise we do a copy
182 if((layer->GetNumOutputSlots() == 1) &&
183 (layer->GetOutputSlots()[0].GetNumConnections() == 1) &&
184 (layer->GetOutputSlots()[0].GetConnection(0)->GetOwningLayer().GetType() == LayerType::Output))
186 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry, workloadFactory, !m_IsExportEnabled);
190 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry, workloadFactory);
196 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
197 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
198 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
201 timelineUtils->CreateTypedEntity(networkGuid, LabelsAndEventClasses::NETWORK_GUID);
202 // Mark the network with a start of life event
203 timelineUtils->RecordEvent(networkGuid, LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
204 // and with the process ID
205 int processID = armnnUtils::Processes::GetCurrentId();
206 std::stringstream ss;
208 timelineUtils->MarkEntityWithLabel(networkGuid, ss.str(), LabelsAndEventClasses::PROCESS_ID_GUID);
211 //Then create workloads.
212 for (auto&& layer : order)
216 // Add layer to the post-optimisation network structure
217 AddLayerStructure(timelineUtils, *layer, networkGuid);
220 const IWorkloadFactory& workloadFactory = GetWorkloadFactory(*layer);
222 switch (layer->GetType())
224 case LayerType::Input:
225 case LayerType::Output:
227 // Inputs and outputs are treated in a special way - see EnqueueInput() and EnqueueOutput().
232 auto workload = layer->CreateWorkload(workloadFactory);
236 const char* const layerName =
237 layer->GetNameStr().length() != 0 ? layer->GetName() : "<Unnamed>";
238 throw InvalidArgumentException(
239 fmt::format("No workload created for layer (name: '{0}' type: '{1}') (compute '{2}')",
240 layerName, static_cast<int>(layer->GetType()), layer->GetBackendId().Get()
246 // Add workload to the post-optimisation network structure
247 AddWorkloadStructure(timelineUtils, workload, *layer);
250 m_WorkloadQueue.push_back(move(workload));
251 // release the constant data in the layer..
252 layer->ReleaseConstantData();
258 for (auto&& workloadFactory : m_WorkloadFactories)
260 workloadFactory.second.first->AfterWorkloadsCreated();
265 // Commit to send the post-optimisation network structure
266 timelineUtils->Commit();
270 m_OptimizedNetwork->GetGraph().AllocateDynamicBuffers();
272 // Now that the intermediate tensor memory has been set-up, do any post allocation configuration for each workload.
273 for (auto& workload : m_WorkloadQueue)
275 workload->PostAllocationConfigure();
279 void LoadedNetwork::SendNetworkStructure()
281 Graph& order = m_OptimizedNetwork->GetGraph().TopologicalSort();
282 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
284 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
285 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
287 timelineUtils->CreateTypedEntity(networkGuid, LabelsAndEventClasses::NETWORK_GUID);
289 for (auto&& layer : order)
291 // Add layer to the post-optimisation network structure
292 AddLayerStructure(timelineUtils, *layer, networkGuid);
293 switch (layer->GetType())
295 case LayerType::Input:
296 case LayerType::Output:
298 // Inputs and outputs are treated in a special way - see EnqueueInput() and EnqueueOutput().
303 for (auto& workload : m_WorkloadQueue)
305 // Add workload to the post-optimisation network structure
306 AddWorkloadStructure(timelineUtils, workload, *layer);
312 // Commit to send the post-optimisation network structure
313 timelineUtils->Commit();
316 profiling::ProfilingGuid LoadedNetwork::GetNetworkGuid()
318 return m_OptimizedNetwork->GetGuid();
321 TensorInfo LoadedNetwork::GetInputTensorInfo(LayerBindingId layerId) const
323 for (auto&& inputLayer : m_OptimizedNetwork->GetGraph().GetInputLayers())
325 ARMNN_ASSERT_MSG(inputLayer->GetNumOutputSlots() == 1, "Input layer should have exactly 1 output slot");
326 if (inputLayer->GetBindingId() == layerId)
328 return inputLayer->GetOutputSlot(0).GetTensorInfo();
332 throw InvalidArgumentException(fmt::format("No input layer is associated with id {}", layerId));
335 TensorInfo LoadedNetwork::GetOutputTensorInfo(LayerBindingId layerId) const
337 for (auto&& outputLayer : m_OptimizedNetwork->GetGraph().GetOutputLayers())
339 ARMNN_ASSERT_MSG(outputLayer->GetNumInputSlots() == 1, "Output layer should have exactly 1 input slot");
340 ARMNN_ASSERT_MSG(outputLayer->GetInputSlot(0).GetConnection(), "Input slot on Output layer must be connected");
341 if (outputLayer->GetBindingId() == layerId)
343 return outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo();
347 throw InvalidArgumentException(fmt::format("No output layer is associated with id {}", layerId));
350 const IWorkloadFactory& LoadedNetwork::GetWorkloadFactory(const Layer& layer) const
352 const IWorkloadFactory* workloadFactory = nullptr;
354 auto it = m_WorkloadFactories.find(layer.GetBackendId());
355 if (it == m_WorkloadFactories.end())
357 throw RuntimeException(fmt::format("No workload factory for {0} to be used for layer: {1}",
358 layer.GetBackendId().Get(),
363 workloadFactory = it->second.first.get();
365 ARMNN_ASSERT_MSG(workloadFactory, "No workload factory");
367 std::string reasonIfUnsupported;
368 ARMNN_ASSERT_MSG(IWorkloadFactory::IsLayerSupported(layer,
371 m_OptimizedNetwork->GetModelOptions()),
372 "Factory does not support layer");
373 IgnoreUnused(reasonIfUnsupported);
374 return *workloadFactory;
379 // Non-copyable class owning accelerator-specific tensor data.
383 TensorPin(std::unique_ptr<ITensorHandle> handle, const TensorInfo& info, LayerBindingId id)
384 : m_TensorHandle(std::move(handle))
390 ITensorHandle* GetTensorHandle() const { return m_TensorHandle.get(); }
391 const TensorInfo& GetTensorInfo() const { return m_TensorInfo; }
392 LayerBindingId GetBindingId() const { return m_Id; }
395 std::unique_ptr<ITensorHandle> m_TensorHandle;
396 TensorInfo m_TensorInfo;
400 static const TensorPin& GetTensorPin(LayerBindingId id,
401 const std::vector<TensorPin>& pins,
402 char const* bindingPointDesc)
404 auto it = std::find_if(pins.begin(), pins.end(),
405 [id](const TensorPin& pin)
407 return pin.GetBindingId() == id;
410 if (it != pins.end())
416 throw InvalidArgumentException(fmt::format("No tensor supplied for {0} {1}", bindingPointDesc, id));
420 // Stores data that needs to be kept accessible for the entire execution of a workload.
424 WorkloadData(const InputTensors& inputTensors, const OutputTensors& outputTensors)
426 m_InputTensorPins.reserve(inputTensors.size());
427 m_OutputTensorPins.reserve(outputTensors.size());
429 for (auto inputTensorPair : inputTensors)
431 auto inputTensor = inputTensorPair.second;
433 std::unique_ptr<ITensorHandle> tensorHandle =
434 std::make_unique<ConstPassthroughCpuTensorHandle>(inputTensor.GetInfo(),inputTensor.GetMemoryArea());
435 LayerBindingId layerId = inputTensorPair.first;
437 m_InputTensorPins.emplace_back(std::move(tensorHandle), inputTensor.GetInfo(), layerId);
440 for (auto outputTensorPair : outputTensors)
442 auto outputTensor = outputTensorPair.second;
444 std::unique_ptr<ITensorHandle> tensorHandle =
445 std::make_unique<PassthroughCpuTensorHandle>(outputTensor.GetInfo(), outputTensor.GetMemoryArea());
446 LayerBindingId layerId = outputTensorPair.first;
448 m_OutputTensorPins.emplace_back(std::move(tensorHandle), outputTensor.GetInfo(), layerId);
452 const TensorPin& GetInputTensorPin(LayerBindingId id) const
454 return GetTensorPin(id, m_InputTensorPins, "input");
457 const TensorPin& GetOutputTensorPin(LayerBindingId id) const
459 return GetTensorPin(id, m_OutputTensorPins, "output");
464 std::vector<TensorPin> m_InputTensorPins;
465 std::vector<TensorPin> m_OutputTensorPins;
470 Status LoadedNetwork::EnqueueWorkload(const InputTensors& inputTensors,
471 const OutputTensors& outputTensors)
473 const Graph& graph = m_OptimizedNetwork->GetGraph();
475 // Walk graph to determine the order of execution.
476 if (graph.GetNumLayers() < 2)
478 ARMNN_LOG(warning) << "IRuntime::EnqueueWorkload()::Less than two nodes in graph";
479 return Status::Failure;
482 // Data that must be kept alive for the entire execution of the workload.
483 WorkloadData workloadData(inputTensors, outputTensors);
485 if (graph.GetNumInputs() != inputTensors.size())
487 throw InvalidArgumentException("Number of inputs provided does not match network.");
490 // For each input to the network, call EnqueueInput with the data passed by the user.
492 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareInputs");
493 m_InputQueue.clear();
494 m_InputQueue.reserve(graph.GetNumInputs());
495 for (const BindableLayer* inputLayer : graph.GetInputLayers())
497 const TensorPin& pin = workloadData.GetInputTensorPin(inputLayer->GetBindingId());
498 EnqueueInput(*inputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
502 // For each output to the network, call EnqueueOutput with the data passed by the user.
504 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareOutputs");
505 m_OutputQueue.clear();
506 m_OutputQueue.reserve(graph.GetNumOutputs());
507 for (const BindableLayer* outputLayer : graph.GetOutputLayers())
509 const TensorPin& pin = workloadData.GetOutputTensorPin(outputLayer->GetBindingId());
510 EnqueueOutput(*outputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
514 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
515 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
516 ProfilingGuid inferenceGuid = m_ProfilingService.GetNextGuid();
519 // Add inference timeline trace if profiling is enabled.
520 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
521 timelineUtils->CreateTypedEntity(inferenceGuid, LabelsAndEventClasses::INFERENCE_GUID);
522 timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
525 LabelsAndEventClasses::EXECUTION_OF_GUID);
526 timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
529 bool executionSucceeded = true;
532 if (m_ProfilingService.IsProfilingEnabled())
534 m_ProfilingService.IncrementCounterValue(armnn::profiling::INFERENCES_RUN);
536 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Execute");
537 ARMNN_SCOPED_HEAP_PROFILING("Executing");
538 executionSucceeded = Execute(timelineUtils, inferenceGuid);
543 // Add end of life of the inference timeline if profiling is enabled.
544 timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
545 timelineUtils->Commit();
547 return executionSucceeded ? Status::Success : Status::Failure;
550 void LoadedNetwork::EnqueueInput(const BindableLayer& layer, ITensorHandle* tensorHandle, const TensorInfo& tensorInfo)
552 if (layer.GetType() != LayerType::Input)
554 throw InvalidArgumentException("EnqueueInput: given layer not an InputLayer");
557 if (tensorHandle == nullptr)
559 throw InvalidArgumentException("EnqueueInput: tensorHandle must not be NULL");
562 InputQueueDescriptor inputQueueDescriptor;
565 inputQueueDescriptor.m_Inputs.push_back(tensorHandle);
566 info.m_InputTensorInfos.push_back(tensorInfo);
568 ARMNN_ASSERT_MSG(layer.GetNumOutputSlots() == 1, "Can only handle Input Layer with one output");
569 const OutputHandler& handler = layer.GetOutputHandler();
570 const TensorInfo& outputTensorInfo = handler.GetTensorInfo();
571 ITensorHandle* outputTensorHandle = handler.GetData();
572 ARMNN_ASSERT_MSG(outputTensorHandle != nullptr,
573 "Data should have been allocated.");
574 inputQueueDescriptor.m_Outputs.push_back(outputTensorHandle);
575 info.m_OutputTensorInfos.push_back(outputTensorInfo);
577 MemorySourceFlags importFlags = outputTensorHandle->GetImportFlags();
578 bool needMemCopy = true;
579 if (m_IsImportEnabled) // Try import the input tensor
581 if(CheckFlag(importFlags, MemorySource::Malloc) )
584 // This assumes a CPU Tensor handle
585 void* mem = tensorHandle->Map(false);
586 if (outputTensorHandle->Import(mem, MemorySource::Malloc))
588 tensorHandle->Unmap();
589 return; // No need for a workload since the import has been done.
591 tensorHandle->Unmap();
592 throw MemoryImportException("EnqueueInput: Memory Import failed");
597 // Create a mem copy workload for input since we did not import
598 std::unique_ptr<IWorkload> inputWorkload = std::make_unique<CopyMemGenericWorkload>(inputQueueDescriptor, info);
600 ARMNN_ASSERT_MSG(inputWorkload, "No input workload created");
602 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
603 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
606 // Add Input Workload to the post-optimisation network structure
607 AddWorkloadStructure(timelineUtils, inputWorkload, layer);
608 timelineUtils->Commit();
611 m_InputQueue.push_back(move(inputWorkload));
615 void LoadedNetwork::EnqueueOutput(const BindableLayer& layer, ITensorHandle* tensorHandle, const TensorInfo& tensorInfo)
617 if (layer.GetType() != LayerType::Output)
619 throw InvalidArgumentException("EnqueueOutput: given layer not an OutputLayer");
622 if (tensorHandle == nullptr)
624 throw InvalidArgumentException("EnqueueOutput: tensorHandle must not be NULL");
627 OutputQueueDescriptor outputQueueDescriptor;
630 outputQueueDescriptor.m_Outputs.push_back(tensorHandle);
631 info.m_OutputTensorInfos.push_back(tensorInfo);
633 ARMNN_ASSERT_MSG(layer.GetNumInputSlots() == 1, "Output Layer should have exactly one input.");
635 // Gets the output handler from the previous node.
636 const OutputHandler& outputHandler = layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetOutputHandler();
638 const TensorInfo& inputTensorInfo = outputHandler.GetTensorInfo();
639 ITensorHandle* inputTensorHandle = outputHandler.GetData();
640 ARMNN_ASSERT_MSG(inputTensorHandle != nullptr, "Data should have been allocated.");
642 // Try import the output tensor.
643 // Note: We can only import the output pointer if all of the following hold true:
644 // a) The imported pointer is aligned sufficiently
645 // b) The tensor has zero padding
646 // c) There is only one connection to the OutputSlot and it is to an OutputLayer.
647 // d) The output pointer is allocated via malloc. (Other types will be supported in a later release)
648 // e) m_IsExportEnabled must be set to true
649 bool needMemCopy = true;
650 if (m_IsExportEnabled && (layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetNumConnections() == 1))
652 if(layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetOwningLayer().GetType() != LayerType::Input)
654 MemorySourceFlags importFlags = inputTensorHandle->GetImportFlags();
655 if (CheckFlag(importFlags, MemorySource::Malloc))
658 void *mem = tensorHandle->Map(false);
659 bool importOk = inputTensorHandle->Import(mem, MemorySource::Malloc);
660 tensorHandle->Unmap();
664 // Insert synchronization workload
665 MemSyncQueueDescriptor syncDesc;
666 syncDesc.m_Inputs.push_back(inputTensorHandle);
667 info.m_InputTensorInfos.push_back(inputTensorInfo);
668 auto syncWorkload = std::make_unique<SyncMemGenericWorkload>(syncDesc, info);
669 ARMNN_ASSERT_MSG(syncWorkload, "No sync workload created");
670 m_OutputQueue.push_back(move(syncWorkload));
674 throw MemoryExportException("EnqueueOutput: Memory Export failed");
681 // If we got here then we didn't export the memory, so add an output workload which performs a memcopy.
682 outputQueueDescriptor.m_Inputs.push_back(inputTensorHandle);
683 info.m_InputTensorInfos.push_back(inputTensorInfo);
685 std::unique_ptr<IWorkload> outputWorkload =
686 std::make_unique<CopyMemGenericWorkload>(outputQueueDescriptor, info);
687 ARMNN_ASSERT_MSG(outputWorkload, "No output workload created");
689 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
690 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
693 // Add Output Workload to the post-optimisation network structure
694 AddWorkloadStructure(timelineUtils, outputWorkload, layer);
695 timelineUtils->Commit();
698 m_OutputQueue.push_back(move(outputWorkload));
702 void LoadedNetwork::AllocateWorkingMemory(std::lock_guard<std::mutex>& lock)
704 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Working Memory Allocation");
706 // this unused parameter makes sure we can only call this function with a valid lock
709 if (m_IsWorkingMemAllocated)
713 for (auto&& workloadFactory : m_WorkloadFactories)
715 IBackendInternal::IMemoryManagerSharedPtr memoryManager = workloadFactory.second.second;
718 memoryManager->Acquire();
721 m_TensorHandleFactoryRegistry.AquireMemory();
722 m_IsWorkingMemAllocated = true;
725 void LoadedNetwork::FreeWorkingMemory()
727 std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
728 if (!m_IsWorkingMemAllocated)
732 // Informs the memory managers to release memory in it's respective memory group
733 for (auto&& workloadFactory : m_WorkloadFactories)
735 IBackendInternal::IMemoryManagerSharedPtr memoryManager = workloadFactory.second.second;
738 memoryManager->Release();
741 m_TensorHandleFactoryRegistry.ReleaseMemory();
742 m_IsWorkingMemAllocated = false;
745 bool LoadedNetwork::Execute(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
746 profiling::ProfilingGuid inferenceGuid)
750 auto Fail = [&](const std::exception& error)
752 ARMNN_LOG(error) << "An error occurred attempting to execute a workload: " << error.what();
758 std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
759 AllocateWorkingMemory(lockGuard);
761 ProfilingDynamicGuid workloadInferenceID(0);
762 auto ExecuteQueue = [&timelineUtils, &workloadInferenceID, &inferenceGuid](WorkloadQueue& queue)
764 for (auto& workload : queue)
768 workloadInferenceID = timelineUtils->RecordWorkloadInferenceAndStartOfLifeEvent(workload->GetGuid(),
774 timelineUtils->RecordEndOfLifeEvent(workloadInferenceID);
779 ExecuteQueue(m_InputQueue);
780 ExecuteQueue(m_WorkloadQueue);
781 ExecuteQueue(m_OutputQueue);
783 catch (const RuntimeException& error)
787 catch (const std::runtime_error& error)
795 void LoadedNetwork::RegisterDebugCallback(const DebugCallbackFunction& func)
797 for (auto&& workloadPtr: m_WorkloadQueue)
799 workloadPtr.get()->RegisterDebugCallback(func);