xref: /aosp_15_r20/external/armnn/src/armnn/Runtime.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2017, 2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "ArmNNProfilingServiceInitialiser.hpp"
7 #include "Runtime.hpp"
8 
9 #include <ProfilingOptionsConverter.hpp>
10 
11 #include <armnn/Version.hpp>
12 #include <armnn/BackendRegistry.hpp>
13 #include <armnn/BackendHelper.hpp>
14 #include <armnn/Logging.hpp>
15 
16 #include <armnn/backends/IBackendContext.hpp>
17 
18 #include <armnn/profiling/ArmNNProfiling.hpp>
19 
20 #include <armnn/utility/PolymorphicDowncast.hpp>
21 #include <armnn/utility/Timer.hpp>
22 
23 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
24 #include <backendsCommon/DynamicBackendUtils.hpp>
25 #endif
26 
27 #include <backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyLibrary.hpp>
28 
29 #include <client/include/backends/IBackendProfiling.hpp>
30 
31 #include <common/include/LabelsAndEventClasses.hpp>
32 
33 #include <iostream>
34 
35 
36 using namespace armnn;
37 using namespace std;
38 
39 namespace armnn
40 {
IRuntime()41 IRuntime::IRuntime() : pRuntimeImpl( new RuntimeImpl(armnn::IRuntime::CreationOptions())) {}
42 
IRuntime(const IRuntime::CreationOptions & options)43 IRuntime::IRuntime(const IRuntime::CreationOptions& options) : pRuntimeImpl(new RuntimeImpl(options)) {}
44 
45 IRuntime::~IRuntime() = default;
46 
CreateRaw(const CreationOptions & options)47 IRuntime* IRuntime::CreateRaw(const CreationOptions& options)
48 {
49     return new IRuntime(options);
50 }
51 
Create(const CreationOptions & options)52 IRuntimePtr IRuntime::Create(const CreationOptions& options)
53 {
54     return IRuntimePtr(CreateRaw(options), &IRuntime::Destroy);
55 }
56 
Destroy(IRuntime * runtime)57 void IRuntime::Destroy(IRuntime* runtime)
58 {
59     delete runtime;
60 }
61 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network)62 Status IRuntime::LoadNetwork(NetworkId& networkIdOut, IOptimizedNetworkPtr network)
63 {
64     return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network));
65 }
66 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network,std::string & errorMessage)67 Status IRuntime::LoadNetwork(NetworkId& networkIdOut,
68                              IOptimizedNetworkPtr network,
69                              std::string& errorMessage)
70 {
71     return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network), errorMessage);
72 }
73 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network,std::string & errorMessage,const INetworkProperties & networkProperties)74 Status IRuntime::LoadNetwork(NetworkId& networkIdOut,
75                              IOptimizedNetworkPtr network,
76                              std::string& errorMessage,
77                              const INetworkProperties& networkProperties)
78 {
79     return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network), errorMessage, networkProperties);
80 }
81 
GetInputTensorInfo(NetworkId networkId,LayerBindingId layerId) const82 armnn::TensorInfo IRuntime::GetInputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
83 {
84     return pRuntimeImpl->GetInputTensorInfo(networkId, layerId);
85 }
86 
GetOutputTensorInfo(NetworkId networkId,LayerBindingId layerId) const87 armnn::TensorInfo IRuntime::GetOutputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
88 {
89     return pRuntimeImpl->GetOutputTensorInfo(networkId, layerId);
90 }
91 
ImportInputs(NetworkId networkId,const InputTensors & inputTensors,MemorySource forceImportMemorySource)92 std::vector<ImportedInputId> IRuntime::ImportInputs(NetworkId networkId, const InputTensors& inputTensors,
93                                                     MemorySource forceImportMemorySource)
94 {
95     return pRuntimeImpl->ImportInputs(networkId, inputTensors, forceImportMemorySource);
96 }
97 
ImportOutputs(NetworkId networkId,const OutputTensors & outputTensors,MemorySource forceImportMemorySource)98 std::vector<ImportedOutputId> IRuntime::ImportOutputs(NetworkId networkId, const OutputTensors& outputTensors,
99                                                       MemorySource forceImportMemorySource)
100 {
101     return pRuntimeImpl->ImportOutputs(networkId, outputTensors, forceImportMemorySource);
102 }
103 
ClearImportedInputs(NetworkId networkId,const std::vector<ImportedInputId> inputIds)104 void IRuntime::ClearImportedInputs(NetworkId networkId, const std::vector<ImportedInputId> inputIds)
105 {
106     return pRuntimeImpl->ClearImportedInputs(networkId, inputIds);
107 }
ClearImportedOutputs(NetworkId networkId,const std::vector<ImportedOutputId> outputIds)108 void IRuntime::ClearImportedOutputs(NetworkId networkId, const std::vector<ImportedOutputId> outputIds)
109 {
110     return pRuntimeImpl->ClearImportedOutputs(networkId, outputIds);
111 }
112 
EnqueueWorkload(NetworkId networkId,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputIds,std::vector<ImportedOutputId> preImportedOutputIds)113 Status IRuntime::EnqueueWorkload(NetworkId networkId,
114                                  const InputTensors& inputTensors,
115                                  const OutputTensors& outputTensors,
116                                  std::vector<ImportedInputId> preImportedInputIds,
117                                  std::vector<ImportedOutputId> preImportedOutputIds)
118 {
119     return pRuntimeImpl->EnqueueWorkload(networkId, inputTensors, outputTensors,
120                                          preImportedInputIds, preImportedOutputIds);
121 }
122 
Execute(IWorkingMemHandle & workingMemHandle,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputs,std::vector<ImportedOutputId> preImportedOutputs)123 Status IRuntime::Execute(IWorkingMemHandle& workingMemHandle,
124                          const InputTensors& inputTensors,
125                          const OutputTensors& outputTensors,
126                          std::vector<ImportedInputId> preImportedInputs,
127                          std::vector<ImportedOutputId> preImportedOutputs)
128 {
129     return pRuntimeImpl->Execute(workingMemHandle,
130                                  inputTensors,
131                                  outputTensors,
132                                  preImportedInputs,
133                                  preImportedOutputs);
134 }
135 
UnloadNetwork(NetworkId networkId)136 Status IRuntime::UnloadNetwork(NetworkId networkId)
137 {
138     return pRuntimeImpl->UnloadNetwork(networkId);
139 }
140 
GetDeviceSpec() const141 const IDeviceSpec& IRuntime::GetDeviceSpec() const
142 {
143     return pRuntimeImpl->GetDeviceSpec();
144 }
145 
CreateWorkingMemHandle(NetworkId networkId)146 std::unique_ptr<IWorkingMemHandle> IRuntime::CreateWorkingMemHandle(NetworkId networkId)
147 {
148     return pRuntimeImpl->CreateWorkingMemHandle(networkId);
149 }
150 
GetProfiler(NetworkId networkId) const151 const std::shared_ptr<IProfiler> IRuntime::GetProfiler(NetworkId networkId) const
152 {
153     return pRuntimeImpl->GetProfiler(networkId);
154 }
155 
RegisterDebugCallback(NetworkId networkId,const DebugCallbackFunction & func)156 void IRuntime::RegisterDebugCallback(NetworkId networkId, const DebugCallbackFunction& func)
157 {
158     return pRuntimeImpl->RegisterDebugCallback(networkId, func);
159 }
160 
GenerateNetworkId()161 int RuntimeImpl::GenerateNetworkId()
162 {
163     return m_NetworkIdCounter++;
164 }
165 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork)166 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut, IOptimizedNetworkPtr inNetwork)
167 {
168     std::string ignoredErrorMessage;
169     return LoadNetwork(networkIdOut, std::move(inNetwork), ignoredErrorMessage);
170 }
171 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage)172 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut,
173                                 IOptimizedNetworkPtr inNetwork,
174                                 std::string& errorMessage)
175 {
176     INetworkProperties networkProperties(
177             false, MemorySource::Undefined, MemorySource::Undefined);
178     return LoadNetwork(networkIdOut, std::move(inNetwork), errorMessage, networkProperties);
179 }
180 
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage,const INetworkProperties & networkProperties)181 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut,
182                                 IOptimizedNetworkPtr inNetwork,
183                                 std::string& errorMessage,
184                                 const INetworkProperties& networkProperties)
185 {
186     // Register the profiler
187     auto profiler = inNetwork->GetProfiler();
188     ProfilerManager::GetInstance().RegisterProfiler(profiler.get());
189 
190     IOptimizedNetwork* rawNetwork = inNetwork.release();
191 
192     networkIdOut = GenerateNetworkId();
193 
194     for (auto&& context : m_BackendContexts)
195     {
196         context.second->BeforeLoadNetwork(networkIdOut);
197     }
198 
199     unique_ptr<LoadedNetwork> loadedNetwork = LoadedNetwork::MakeLoadedNetwork(
200         std::unique_ptr<IOptimizedNetwork>(rawNetwork),
201         errorMessage,
202         networkProperties,
203         m_ProfilingService.get());
204 
205     if (!loadedNetwork)
206     {
207         return Status::Failure;
208     }
209 
210     {
211 #if !defined(ARMNN_DISABLE_THREADS)
212         std::lock_guard<std::mutex> lockGuard(m_Mutex);
213 #endif
214 
215         // Stores the network
216         m_LoadedNetworks[networkIdOut] = std::move(loadedNetwork);
217     }
218 
219     for (auto&& context : m_BackendContexts)
220     {
221         context.second->AfterLoadNetwork(networkIdOut);
222     }
223 
224     if (m_ProfilingService->IsProfilingEnabled())
225     {
226         m_ProfilingService->IncrementCounterValue(arm::pipe::NETWORK_LOADS);
227     }
228 
229     return Status::Success;
230 }
231 
UnloadNetwork(NetworkId networkId)232 Status RuntimeImpl::UnloadNetwork(NetworkId networkId)
233 {
234     bool unloadOk = true;
235     for (auto&& context : m_BackendContexts)
236     {
237         unloadOk &= context.second->BeforeUnloadNetwork(networkId);
238     }
239 
240     if (!unloadOk)
241     {
242         ARMNN_LOG(warning) << "RuntimeImpl::UnloadNetwork(): failed to unload "
243                               "network with ID:" << networkId << " because BeforeUnloadNetwork failed";
244         return Status::Failure;
245     }
246 
247     std::unique_ptr<arm::pipe::TimelineUtilityMethods> timelineUtils =
248         arm::pipe::TimelineUtilityMethods::GetTimelineUtils(*m_ProfilingService.get());
249     {
250 #if !defined(ARMNN_DISABLE_THREADS)
251         std::lock_guard<std::mutex> lockGuard(m_Mutex);
252 #endif
253 
254         // If timeline recording is on mark the Network end of life
255         if (timelineUtils)
256         {
257             auto search = m_LoadedNetworks.find(networkId);
258             if (search != m_LoadedNetworks.end())
259             {
260                 arm::pipe::ProfilingGuid networkGuid = search->second->GetNetworkGuid();
261                 timelineUtils->RecordEvent(networkGuid,
262                                            arm::pipe::LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
263             }
264         }
265 
266         if (m_LoadedNetworks.erase(networkId) == 0)
267         {
268             ARMNN_LOG(warning) << "WARNING: RuntimeImpl::UnloadNetwork(): " << networkId << " not found!";
269             return Status::Failure;
270         }
271 
272         if (m_ProfilingService->IsProfilingEnabled())
273         {
274             m_ProfilingService->IncrementCounterValue(arm::pipe::NETWORK_UNLOADS);
275         }
276     }
277 
278     for (auto&& context : m_BackendContexts)
279     {
280         context.second->AfterUnloadNetwork(networkId);
281     }
282 
283     // Unregister the profiler
284     ProfilerManager::GetInstance().RegisterProfiler(nullptr);
285 
286     ARMNN_LOG(debug) << "RuntimeImpl::UnloadNetwork(): Unloaded network with ID: " << networkId;
287     return Status::Success;
288 }
289 
GetProfiler(NetworkId networkId) const290 const std::shared_ptr<IProfiler> RuntimeImpl::GetProfiler(NetworkId networkId) const
291 {
292     auto it = m_LoadedNetworks.find(networkId);
293     if (it != m_LoadedNetworks.end())
294     {
295         auto& loadedNetwork = it->second;
296         return loadedNetwork->GetProfiler();
297     }
298 
299     return nullptr;
300 }
301 
ReportStructure(arm::pipe::IProfilingService & profilingService)302 void RuntimeImpl::ReportStructure(arm::pipe::IProfilingService& profilingService)
303 {
304     if (profilingService.IsProfilingEnabled())
305     {
306         LoadedNetworks::iterator it = m_LoadedNetworks.begin();
307         while (it != m_LoadedNetworks.end())
308         {
309             auto& loadedNetwork = it->second;
310             loadedNetwork->SendNetworkStructure(profilingService);
311             // Increment the Iterator to point to next entry
312             it++;
313         }
314     }
315 }
316 
InitialiseProfilingService(arm::pipe::IProfilingService & profilingService)317 void RuntimeImpl::InitialiseProfilingService(arm::pipe::IProfilingService& profilingService)
318 {
319     ArmNNProfilingServiceInitialiser initialiser;
320     initialiser.InitialiseProfilingService(profilingService);
321 }
322 
RuntimeImpl(const IRuntime::CreationOptions & options)323 RuntimeImpl::RuntimeImpl(const IRuntime::CreationOptions& options)
324     : m_NetworkIdCounter(0)
325 {
326     m_ProfilingService = arm::pipe::IProfilingService::CreateProfilingService(
327         arm::pipe::MAX_ARMNN_COUNTER,
328         *this,
329         arm::pipe::ARMNN_SOFTWARE_INFO,
330         arm::pipe::ARMNN_SOFTWARE_VERSION,
331         arm::pipe::ARMNN_HARDWARE_VERSION,
332         *this);
333     const auto start_time = armnn::GetTimeNow();
334     ARMNN_LOG(info) << "ArmNN v" << ARMNN_VERSION;
335     if ( options.m_ProfilingOptions.m_TimelineEnabled && !options.m_ProfilingOptions.m_EnableProfiling )
336     {
337         throw RuntimeException(
338                 "It is not possible to enable timeline reporting without profiling being enabled");
339     }
340 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
341     // Load any available/compatible dynamic backend before the runtime
342     // goes through the backend registry
343     LoadDynamicBackends(options.m_DynamicBackendsPath);
344 #endif
345     armnn::BackendIdSet supportedBackends;
346     for (const auto& id : BackendRegistryInstance().GetBackendIds())
347     {
348         // Store backend contexts for the supported ones
349         try {
350             auto factoryFun = BackendRegistryInstance().GetFactory(id);
351             ARMNN_ASSERT(factoryFun != nullptr);
352             auto backend = factoryFun();
353             ARMNN_ASSERT(backend != nullptr);
354             ARMNN_ASSERT(backend.get() != nullptr);
355 
356             auto customAllocatorMapIterator = options.m_CustomAllocatorMap.find(id);
357             if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end() &&
358                 customAllocatorMapIterator->second == nullptr)
359             {
360 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
361                 // We need to manually clean up  the dynamic backends before throwing an exception.
362                 DynamicBackendUtils::DeregisterDynamicBackends(m_DeviceSpec.GetDynamicBackends());
363                 m_DeviceSpec.ClearDynamicBackends();
364 #endif
365                 throw armnn::Exception("Allocator associated with id " + id.Get() + " is null");
366             }
367 
368             // If the runtime is created in protected mode only add backends that support this mode
369             if (options.m_ProtectedMode)
370             {
371                 // check if backend supports ProtectedMode
372                 using BackendCapability = BackendOptions::BackendOption;
373                 BackendCapability protectedContentCapability {"ProtectedContentAllocation", true};
374                 if (!HasCapability(protectedContentCapability, id))
375                 {
376                     // Protected Content Allocation is not supported by the backend
377                     // backend should not be registered
378                     ARMNN_LOG(warning) << "Backend "
379                                        << id
380                                        << " is not registered as does not support protected content allocation.";
381                     continue;
382                 }
383                 // The user is responsible to provide a custom memory allocator which allows to allocate
384                 // protected memory
385                 if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end())
386                 {
387                     std::string err;
388                     if (customAllocatorMapIterator->second->GetMemorySourceType()
389                         == armnn::MemorySource::DmaBufProtected)
390                     {
391                         if (!backend->UseCustomMemoryAllocator(customAllocatorMapIterator->second, err))
392                         {
393                             ARMNN_LOG(error) << "The backend "
394                                              << id
395                                              << " reported an error when entering protected mode. Backend won't be"
396                                              << " used. ErrorMsg: " << err;
397                             continue;
398                         }
399                         // No errors so register the Custom Allocator with the BackendRegistry
400                         BackendRegistryInstance().RegisterAllocator(id, customAllocatorMapIterator->second);
401                         m_AllocatorsAddedByThisRuntime.emplace(id);
402                     }
403                     else
404                     {
405                         ARMNN_LOG(error) << "The CustomAllocator provided with the runtime options doesn't support "
406                                      "protected memory. Protected mode can't be activated. The backend "
407                                      << id
408                                      << " is not going to be used. MemorySource must be MemorySource::DmaBufProtected";
409                         continue;
410                     }
411                 }
412                 else
413                 {
414                     ARMNN_LOG(error) << "Protected mode can't be activated for backend: "
415                                      << id
416                                      << " no custom allocator was provided to the runtime options.";
417                     continue;
418                 }
419             }
420             else
421             {
422                 // If a custom memory allocator is provided make the backend use that instead of the default
423                 if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end())
424                 {
425                     std::string err;
426                     if (!backend->UseCustomMemoryAllocator(customAllocatorMapIterator->second, err))
427                     {
428                         ARMNN_LOG(error) << "The backend "
429                                          << id
430                                          << " reported an error when trying to use the provided custom allocator."
431                                             " Backend won't be used."
432                                          << " ErrorMsg: " << err;
433                         continue;
434                     }
435                     // No errors so register the Custom Allocator with the BackendRegistry
436                     BackendRegistryInstance().RegisterAllocator(id, customAllocatorMapIterator->second);
437                     m_AllocatorsAddedByThisRuntime.emplace(id);
438                 }
439             }
440 
441             // check if custom memory optimizer strategy map is set
442             if (!options.m_MemoryOptimizerStrategyMap.empty())
443             {
444                 auto customMemoryOptimizerStrategyMapIterator = options.m_MemoryOptimizerStrategyMap.find(id);
445                 // if a memory optimizer strategy is provided make the backend use that instead of the default
446                 if (customMemoryOptimizerStrategyMapIterator != options.m_MemoryOptimizerStrategyMap.end())
447                 {
448                     // no errors.. register the memory optimizer strategy with the BackendRegistry
449                     BackendRegistryInstance().RegisterMemoryOptimizerStrategy(
450                         id, customMemoryOptimizerStrategyMapIterator->second);
451 
452                     ARMNN_LOG(info) << "MemoryOptimizerStrategy  "
453                                     << customMemoryOptimizerStrategyMapIterator->second->GetName()
454                                     << " set for the backend " << id << ".";
455                 }
456             }
457             else
458             {
459                 // check if to use one of the existing memory optimizer strategies is set
460                 std::string memoryOptimizerStrategyName = "";
461                 ParseOptions(options.m_BackendOptions, id, [&](std::string name, const BackendOptions::Var& value)
462                 {
463                     if (name == "MemoryOptimizerStrategy")
464                     {
465                         memoryOptimizerStrategyName = ParseStringBackendOption(value, "");
466                     }
467                 });
468                 if (memoryOptimizerStrategyName != "")
469                 {
470                     std::shared_ptr<IMemoryOptimizerStrategy> strategy =
471                             GetMemoryOptimizerStrategy(memoryOptimizerStrategyName);
472 
473                     if (!strategy)
474                     {
475                         ARMNN_LOG(warning) << "MemoryOptimizerStrategy: " << memoryOptimizerStrategyName
476                                            << " was not found.";
477                     }
478                     else
479                     {
480                         using BackendCapability = BackendOptions::BackendOption;
481                         auto strategyType = GetMemBlockStrategyTypeName(strategy->GetMemBlockStrategyType());
482                         BackendCapability memOptimizeStrategyCapability {strategyType, true};
483                         if (HasCapability(memOptimizeStrategyCapability, id))
484                         {
485                             BackendRegistryInstance().RegisterMemoryOptimizerStrategy(id, strategy);
486 
487                             ARMNN_LOG(info) << "MemoryOptimizerStrategy: "
488                                             << memoryOptimizerStrategyName << " set for the backend " << id << ".";
489                         }
490                         else
491                         {
492                             ARMNN_LOG(warning) << "Backend "
493                                                << id
494                                                << " does not have multi-axis packing capability and cannot support"
495                                                << "MemoryOptimizerStrategy: " << memoryOptimizerStrategyName << ".";
496                         }
497                     }
498                 }
499             }
500 
501             auto context = backend->CreateBackendContext(options);
502 
503             // backends are allowed to return nullptrs if they
504             // don't wish to create a backend specific context
505             if (context)
506             {
507                 m_BackendContexts.emplace(std::make_pair(id, std::move(context)));
508             }
509             supportedBackends.emplace(id);
510 
511             unique_ptr<arm::pipe::IBackendProfiling> profilingIface =
512                 arm::pipe::IBackendProfiling::CreateBackendProfiling(
513                     arm::pipe::ConvertExternalProfilingOptions(options.m_ProfilingOptions),
514                     *m_ProfilingService.get(),
515                     id.Get());
516 
517             // Backends may also provide a profiling context. Ask for it now.
518             auto profilingContext = backend->CreateBackendProfilingContext(options, profilingIface);
519             // Backends that don't support profiling will return a null profiling context.
520             if (profilingContext)
521             {
522                 // Pass the context onto the profiling service.
523                 m_ProfilingService->AddBackendProfilingContext(id, profilingContext);
524             }
525         }
526         catch (const BackendUnavailableException&)
527         {
528             // Ignore backends which are unavailable
529         }
530     }
531 
532     BackendRegistryInstance().SetProfilingService(*m_ProfilingService.get());
533     // pass configuration info to the profiling service
534     m_ProfilingService->ConfigureProfilingService(
535         arm::pipe::ConvertExternalProfilingOptions(options.m_ProfilingOptions));
536     if (options.m_ProfilingOptions.m_EnableProfiling)
537     {
538         // try to wait for the profiling service to initialise
539         m_ProfilingService->WaitForProfilingServiceActivation(3000);
540     }
541 
542     m_DeviceSpec.AddSupportedBackends(supportedBackends);
543 
544     ARMNN_LOG(info) << "Initialization time: " << std::setprecision(2)
545                     << std::fixed << armnn::GetTimeDuration(start_time).count() << " ms.";
546 }
547 
~RuntimeImpl()548 RuntimeImpl::~RuntimeImpl()
549 {
550     const auto startTime = armnn::GetTimeNow();
551     std::vector<int> networkIDs;
552     try
553     {
554         // Coverity fix: The following code may throw an exception of type std::length_error.
555         std::transform(m_LoadedNetworks.begin(), m_LoadedNetworks.end(),
556                        std::back_inserter(networkIDs),
557                        [](const auto &pair) { return pair.first; });
558     }
559     catch (const std::exception& e)
560     {
561         // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
562         // exception of type std::length_error.
563         // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
564         std::cerr << "WARNING: An error has occurred when getting the IDs of the networks to unload: " << e.what()
565                   << "\nSome of the loaded networks may not be unloaded" << std::endl;
566     }
567     // We then proceed to unload all the networks which IDs have been appended to the list
568     // up to the point the exception was thrown (if any).
569 
570     for (auto networkID : networkIDs)
571     {
572         try
573         {
574             // Coverity fix: UnloadNetwork() may throw an exception of type std::length_error,
575             // boost::log::v2s_mt_posix::odr_violation or boost::log::v2s_mt_posix::system_error
576             UnloadNetwork(networkID);
577         }
578         catch (const std::exception& e)
579         {
580             // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
581             // exception of type std::length_error.
582             // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
583             std::cerr << "WARNING: An error has occurred when unloading network " << networkID << ": " << e.what()
584                       << std::endl;
585         }
586     }
587 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
588     // Clear all dynamic backends.
589     DynamicBackendUtils::DeregisterDynamicBackends(m_DeviceSpec.GetDynamicBackends());
590     m_DeviceSpec.ClearDynamicBackends();
591 #endif
592     m_BackendContexts.clear();
593 
594     BackendRegistryInstance().SetProfilingService(armnn::EmptyOptional());
595     // Remove custom allocators that this runtime has added.
596     // Note: that as backends can be per process and there can be many instances of a runtime in a process an allocator
597     // may have been overwritten by another runtime.
598     for_each(m_AllocatorsAddedByThisRuntime.begin(), m_AllocatorsAddedByThisRuntime.end(),
599              [](BackendId id) {BackendRegistryInstance().DeregisterAllocator(id);});
600 
601     ARMNN_LOG(info) << "Shutdown time: " << std::setprecision(2)
602                     << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
603 }
604 
GetLoadedNetworkPtr(NetworkId networkId) const605 LoadedNetwork* RuntimeImpl::GetLoadedNetworkPtr(NetworkId networkId) const
606 {
607 #if !defined(ARMNN_DISABLE_THREADS)
608     std::lock_guard<std::mutex> lockGuard(m_Mutex);
609 #endif
610     return m_LoadedNetworks.at(networkId).get();
611 }
612 
GetInputTensorInfo(NetworkId networkId,LayerBindingId layerId) const613 TensorInfo RuntimeImpl::GetInputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
614 {
615     return GetLoadedNetworkPtr(networkId)->GetInputTensorInfo(layerId);
616 }
617 
GetOutputTensorInfo(NetworkId networkId,LayerBindingId layerId) const618 TensorInfo RuntimeImpl::GetOutputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
619 {
620     return GetLoadedNetworkPtr(networkId)->GetOutputTensorInfo(layerId);
621 }
622 
ImportInputs(NetworkId networkId,const InputTensors & inputTensors,MemorySource forceImportMemorySource)623 std::vector<ImportedInputId> RuntimeImpl::ImportInputs(NetworkId networkId, const InputTensors& inputTensors,
624                                                        MemorySource forceImportMemorySource)
625 {
626     return GetLoadedNetworkPtr(networkId)->ImportInputs(inputTensors, forceImportMemorySource);
627 }
628 
ImportOutputs(NetworkId networkId,const OutputTensors & outputTensors,MemorySource forceImportMemorySource)629 std::vector<ImportedOutputId> RuntimeImpl::ImportOutputs(NetworkId networkId, const OutputTensors& outputTensors,
630                                                          MemorySource forceImportMemorySource)
631 {
632     return GetLoadedNetworkPtr(networkId)->ImportOutputs(outputTensors, forceImportMemorySource);
633 }
634 
ClearImportedInputs(NetworkId networkId,const std::vector<ImportedInputId> inputIds)635 void RuntimeImpl::ClearImportedInputs(NetworkId networkId, const std::vector<ImportedInputId> inputIds)
636 {
637     return GetLoadedNetworkPtr(networkId)->ClearImportedInputs(inputIds);
638 }
ClearImportedOutputs(NetworkId networkId,const std::vector<ImportedOutputId> outputIds)639 void RuntimeImpl::ClearImportedOutputs(NetworkId networkId, const std::vector<ImportedOutputId> outputIds)
640 {
641     return GetLoadedNetworkPtr(networkId)->ClearImportedOutputs(outputIds);
642 }
643 
EnqueueWorkload(NetworkId networkId,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputIds,std::vector<ImportedOutputId> preImportedOutputIds)644 Status RuntimeImpl::EnqueueWorkload(NetworkId networkId,
645                                 const InputTensors& inputTensors,
646                                 const OutputTensors& outputTensors,
647                                 std::vector<ImportedInputId> preImportedInputIds,
648                                 std::vector<ImportedOutputId> preImportedOutputIds)
649 {
650     const auto startTime = armnn::GetTimeNow();
651 
652     LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
653 
654     if (!loadedNetwork)
655     {
656         ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
657         return Status::Failure;
658     }
659     if (loadedNetwork->IsAsyncEnabled())
660     {
661         ARMNN_LOG(error) << "Network " << networkId << " is async enabled.";
662         return Status::Failure;
663     }
664     ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
665 
666     ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "EnqueueWorkload");
667 
668     static thread_local NetworkId lastId = networkId;
669     if (lastId != networkId)
670     {
671         LoadedNetworkFuncSafe(lastId, [](LoadedNetwork* network)
672             {
673                 network->FreeWorkingMemory();
674             });
675     }
676     lastId=networkId;
677 
678     auto status = loadedNetwork->EnqueueWorkload(inputTensors, outputTensors,
679                                                  preImportedInputIds, preImportedOutputIds);
680 
681 
682     // Check if we imported, if not there's no need to call the After EnqueueWorkload events
683     if (!preImportedInputIds.empty() || !preImportedOutputIds.empty())
684     {
685         // Call After EnqueueWorkload events
686         for (auto&& context : m_BackendContexts)
687         {
688             context.second->AfterEnqueueWorkload(networkId);
689         }
690     }
691     ARMNN_LOG(info) << "Execution time: " << std::setprecision(2)
692                     << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
693     return status;
694 }
695 
Execute(IWorkingMemHandle & iWorkingMemHandle,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputs,std::vector<ImportedOutputId> preImportedOutputs)696 Status RuntimeImpl::Execute(IWorkingMemHandle& iWorkingMemHandle,
697                             const InputTensors& inputTensors,
698                             const OutputTensors& outputTensors,
699                             std::vector<ImportedInputId> preImportedInputs,
700                             std::vector<ImportedOutputId> preImportedOutputs)
701 {
702     const auto startTime = armnn::GetTimeNow();
703 
704     NetworkId networkId = iWorkingMemHandle.GetNetworkId();
705     LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
706 
707     if (!loadedNetwork)
708     {
709         ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
710         return Status::Failure;
711     }
712     if (!loadedNetwork->IsAsyncEnabled())
713     {
714         ARMNN_LOG(error) << "Attempting execute " << networkId << " when it is not async enabled.";
715         return Status::Failure;
716     }
717     ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
718 
719     ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Execute");
720 
721     auto status = loadedNetwork->Execute(inputTensors,
722                                          outputTensors,
723                                          iWorkingMemHandle,
724                                          preImportedInputs,
725                                          preImportedOutputs);
726 
727     ARMNN_LOG(info) << "Execution time: " << std::setprecision(2)
728                     << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
729 
730     return status;
731 }
732 
733 /// Create a new unique WorkingMemHandle object. Create multiple handles if you wish to have
734 /// overlapped Execution by calling this function from different threads.
CreateWorkingMemHandle(NetworkId networkId)735 std::unique_ptr<IWorkingMemHandle> RuntimeImpl::CreateWorkingMemHandle(NetworkId networkId)
736 {
737     LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
738 
739     if (!loadedNetwork)
740     {
741         ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
742         return nullptr;
743     }
744     if (!loadedNetwork->IsAsyncEnabled())
745     {
746         ARMNN_LOG(error) << "Network " << networkId << " is not async enabled.";
747         return nullptr;
748     }
749     ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
750 
751     ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "CreateWorkingMemHandle");
752 
753     static thread_local NetworkId lastId = networkId;
754     if (lastId != networkId)
755     {
756         LoadedNetworkFuncSafe(lastId, [](LoadedNetwork* network)
757         {
758             network->FreeWorkingMemory();
759         });
760     }
761     lastId=networkId;
762 
763     return loadedNetwork->CreateWorkingMemHandle(networkId);
764 }
765 
RegisterDebugCallback(NetworkId networkId,const DebugCallbackFunction & func)766 void RuntimeImpl::RegisterDebugCallback(NetworkId networkId, const DebugCallbackFunction& func)
767 {
768     LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
769     loadedNetwork->RegisterDebugCallback(func);
770 }
771 
772 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
LoadDynamicBackends(const std::string & overrideBackendPath)773 void RuntimeImpl::LoadDynamicBackends(const std::string& overrideBackendPath)
774 {
775     // Get the paths where to load the dynamic backends from
776     std::vector<std::string> backendPaths = DynamicBackendUtils::GetBackendPaths(overrideBackendPath);
777 
778     // Get the shared objects to try to load as dynamic backends
779     std::vector<std::string> sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths);
780 
781     // Create a list of dynamic backends
782     m_DynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects);
783 
784     // Register the dynamic backends in the backend registry
785     armnn::BackendIdSet registeredBackendIds = DynamicBackendUtils::RegisterDynamicBackends(m_DynamicBackends);
786 
787     // Add the registered dynamic backend ids to the list of supported backends
788     m_DeviceSpec.AddSupportedBackends(registeredBackendIds, true);
789 }
790 #endif
791 } // namespace armnn
792